home *** CD-ROM | disk | FTP | other *** search
/ TeX 1995 July / TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO / dviware / ln03 / rmcs / dvitoln03.web < prev    next >
Text File  |  1991-09-29  |  451KB  |  10,434 lines

  1. % This program by D. E. Knuth is not copyrighted and can be used freely.
  2. % Version 1 was completed in September, 1982.
  3. % Slight changes were made in October, 1982, for version 0.7 of TeX.
  4. % Version 1.1 corrected minor bugs (May, 1983).
  5. % Version 2 was released with version 0.999 of TeX (July, 1983).
  6. % Version 2.1 corrected a bug when no fonts are present (September, 1983).
  7. % Version 2.2 corrected bugs in max_*_so_far and put1 (October, 1983).
  8. % Version 2.3 corrected spacing of accents (March, 1984).
  9. % Version 2.4 fixed rounding, changed oriental font conventions (April, 1984).
  10. % Version 2.5 improved the case of zero pages (May, 1984).
  11. % Version 2.6 introduced max_drift (June, 1984).
  12. % Version 2.7 had minor editorial changes (August, 1984).
  13.  
  14. % Version 1.0 of DVItoLN03 was created (as a change file to DVItype) March 1986
  15. %             by Brian HAMILTON KELLY, Royal Military College of Science,
  16. %             Shrivenham, SWINDON, SN6 8LA, United Kingdom
  17. %             Tel: Swindon (0793) 785252 (Int'l +44-793-785252)
  18. %             e-mail: (Janet) tex@@uk.ac.cranfield.rmcs
  19. %
  20. %             This drew heavily upon Flavio Rose's DVI2LNG program, but also
  21. %             included the work formerly performed as a separate pass by his
  22. %             LN03TOPP program (which had bugs, and was written in PL/I, for
  23. %             which we did not have a compiler!)
  24. % Version 1.1 corrected minor bugs in the port from PL/I and displayed page
  25. %             numbers as each was processed  April 1986
  26. % Version 1.2 used .PXL files in separate directories for each magnification,
  27. %             in the manner of Andrew Trevorrow's DVItoVDU  July 1986
  28. % Version 1.3 introduced minor cosmetic changes and increased |max_rules| to
  29. %             2048 July 1987
  30. % Version 1.4 added support for including sixel graphics
  31. %             through \special Sep 87
  32. % Version 1.5 merged the original DVItype.WEB and the (now hopelessly huge)
  33. %             change file, and `WEBified' the whole system.  Also added proper
  34. %             support for default filenames, so that one CAN reference an
  35. %             older DVI file than the latest version.
  36. % Version 1.6 supports oversized glyphs (by making them into invisible
  37. %             characters) and also outputs invisible characters as a single
  38. %             pixel high row of white pixels of an appropriate width
  39. % Version 1.7 prints the glyphs of oversized characters as bitmap sixel dumps.
  40. % Version 2.0 reads PK files instead of .PXL (if available)
  41. % Version 2.1 corrects error reading invisible fonts from packed files and
  42. %             with invisible characters of zero TFM width.
  43. % Version 3.0 includes wholesale rearrangement of some sections of code, to
  44. %             improve its WEB-like quality.  It also adds the following
  45. %             functionality:
  46. %                 The packed and unpacked font files may be provided in
  47. %                 either a flat or a rooted directory structure; if logical
  48. %                 names are used to specify these locations (as in the .CLD
  49. %                 file provided), the files may be spread over a number of
  50. %                 different directories or volumes.
  51. %                 The error messages are improved over earlier versions of
  52. %                 the program, and are now all indexed in the woven
  53. %                 (WEAVEd?) WEB.
  54. %                 The program can now handle fonts with more then 128
  55. %                 characters, up to TeX's limit of 256.  Therefore, it can
  56. %                 now process documents which use Silvio Levy's Greek fonts.
  57. %                 Retention of the log (.TYP) file may now be forced,
  58. %                 suppressed, or allowed to be determined by the success of
  59. %                 the processing.
  60. %                 Minor revisions and corrections have been made, in
  61. %                 particular, it now correctly understands the physical
  62. %                 limitations to the imaging area.
  63. % Version 3.1 correctly computes the magnification of each font for the font
  64. %             loading reports, and also ensures that all output files are
  65. %             opened in the current default directory, even when logical
  66. %             names are used for the input file.  It also extracts and
  67. %             reports the full file specification of the source (.DVI) file.
  68. %             Also introduced the /OUTPUT qualifier and file spec on /LOG.
  69. % Version 3.1-1 adds the `special' commands that were in the original Rose
  70. %               program; this code was developed independently of (and
  71. %               simultaneously with) version 3.1 (see above), and it therefore
  72. %               seemed appropriate to give it a DEC-style version number
  73. %               extension...
  74. % Version 3.1-2 increases |max_rules| to 16384 to cope with the output of
  75. %               BIGTeX.
  76. % Version 3.1-3 reports an error status if the opening of a \special sixel
  77. %               file fails.  The LN03 is also reset to pixel spacing units
  78. %               after the sixel file has been included.
  79. % Version 3.1-4 avoids attempting to print non-existent glyphs; some font
  80. %               designers use non-existent characters in ligature sequences
  81. %               (for example, Georgia Tobin ligatures * with characters such
  82. %               as i, j, r, s, to select a variant form of the letter).
  83. %               With earlier versions of this program, having failed to
  84. %               download a glyph for the non-existent character, an attempt
  85. %               would be made to print it is a bitmap when processing the
  86. %               pages.
  87. % Version 3.2   This version, and all others prior to V4.0, were never
  88. %               released: they were purely internal to RMCS whilst
  89. %               additional functionality was being incorporated.
  90. %               This version tidied up Robin Fairbairns' code, particularly
  91. %               for recognizing <dimen> units.
  92. % Version 3.2-1 Correct code so that the arguments of \special are treated
  93. %               as |ASCII_code| (they are not *necessarily* |char|s).
  94. % Version 3.3   Provide qualifiers to define all logicals used, so that
  95. %               sites may chose to use TEX$FONTS or TEX_FONTS, etc.
  96. %               Permit use of units other than pixel for specification
  97. %               of /LEFT_MARGIN and /TOP_MARGIN.
  98. % Version 3.3-1 Permit /LEFT_MARGIN and /TOP_MARGIN to take dimensionless
  99. %               quantity, with the assumption that it's in pixels.
  100. % Version 3.4   Look out for virtual fonts, and reject them
  101. % Version 3.4-1 Read VF file, and parse it
  102. % Version 3.4-2 Map virtual font characters to real fonts, and load them
  103. % Version 3.4-3 Now able to print simple characters from a VF
  104. % Version 3.5   Made do_page capable of recursion to support complex
  105. %               sequences in VF files
  106. % Version 3.5-1 Revised DVI reading code for input from |vf|
  107. % Version 3.5-2 Scaled |dvi| movements taken from VF file
  108. % Version 3.5-3 Corrected computation of scaling of -ve movements
  109. % Version 3.5-4 Removed confusion caused by empty segments on |seg_list|
  110. % Version 3.5-5 Use font_scaled_size[vfno] for scaling physical fonts!
  111. % Version 3.5-6 Corrected storage of -ve parameters in |vf| array
  112. % Version 3.5-7 Terminate normal text segment preceding composite character
  113. % Version 3.6   Read TFM files only once; fold upper/lower case names
  114. % Version 3.6-1 Guard against infinite recursion with font recurse
  115. % Version 3.6-2 Reference to another virtual font can't map to LN03 font
  116. % Version 3.6-3 Correct spurious checksum error
  117. % Version 3.7   Supports landscape and portrait \specials
  118. % Version 3.7-1 Defers bitmap output of glyphs until |eop|, like rest
  119. % Version 3.7-2 Correct handling of bitmap glyphs in |out_list|
  120. % Version 3.8   Support mixture of orientations on same page
  121. % Version 3.8-1 Moved all |max...| definitions to near front of WEB
  122. % Version 3.8-2 Changed |varying [name_size] of char| into |file_spec|
  123. % Version 3.8-3 Included narrative describing TFM files
  124. % Version 3.8-4 Aggregate all |glyph_info| into one vector |glyphs|
  125. % Version 3.8-5 Cosmetic changes; errors/warnings not just to TYP file
  126. % Version 3.8-6 Preliminary moves to set rules for missing fonts
  127. % Version 3.9   Image glyph from missing font by a solid rule
  128. % Version 3.9-1 Integrate |pixel_width|, etc into |glyph_map|
  129. % Version 3.9-2 Integrate |width|, etc into |glyph_map|
  130. % Version 3.9-3 Further speed-up, by greater use of |with glyph_map|
  131. % Version 3.9-4 Speed up setting loop, by removing double indexing
  132. % Version 3.9-5 Defer text too wide message until eop; report phases
  133. % Version 3.9-6 Couple more little speed-ups, missed before
  134. % Version 3.9-7 Cosmetic amendments; improved indexing
  135. % Version 3.9-8 Move <Move to top of form> before Copy_sixel_file
  136. % Version 3.91  Start unravelling |print_mode| and trays at top of form
  137. % Version 3.91-1 Acknowledge efforts of Robin and Karsten
  138. % Version 3.91-2 Ensure |last_page_recto| set even first time through
  139. % Version 3.91-3 Use Karsten's |blank_follows| mechanism
  140. % Version 3.91-4 Trim trailing spaces from |cur_name| in error message
  141. % Version 3.91-5 Determine |ln03_master| from /DEVICE_TYPE setting
  142. % Version 3.91-6 Provide |rd_scaled_pt|, for decoding /hfuzz, /vfuzz
  143. % Version 3.91-7 In duplex, don't change tray until 3rd page
  144. % Version 3.91-8 Insert sixel dumps into |seg_list|
  145. % Version 4.0    Cosmetic changes to error messages - new release!!!
  146.  
  147. \outer\def\N#1.#2.{\MN#1.\vfil\eject % beginning of starred section
  148.   \def\rhead{\uppercase{\ignorespaces#2}} % define running headline
  149.   \message{*\modno} % progress report
  150.   {\def\.{\string\.}\def\BS{\string\BS\space}% Prevent expansion during contents write
  151.     \edef\next{\write\cont{\Z{#2}{\modno}{\the\pageno}}}\next % to contents file
  152.   }%
  153.   \ifon\startsection{\bf\ignorespaces#2.\quad}\ignorespaces}
  154.  
  155.  
  156. % Here is TeX material that gets inserted after \input webmac
  157. \def\hang{\hangindent 3em\indent\ignorespaces}
  158. \font\ninerm=cmr9
  159. \let\mc=\ninerm % medium caps for names like PASCAL
  160. \font\logo=logo10 % font used for the METAFONT logo
  161. \def\MF{{\logo META}\-{\logo FONT}}
  162. \def\PASCAL{P{\mc ASCAL}}
  163.  
  164. \def\(#1){} % this is used to make section names sort themselves better
  165. \def\9#1{} % this is used for sort keys in the index
  166.  
  167. \def\title{DVI$\,$\lowercase{to}$\,$LN03 V4.0}
  168. \def\contentspagenumber{0}
  169. \def\topofcontents{\null
  170.   \def\titlepage{F} % include headline on the contents page
  171.   \def\rheader{\mainfont\hfil \contentspagenumber}
  172.   \vfill
  173.   \centerline{\titlefont The {\ttitlefont DVItoLN03} processor}
  174.   \vskip 15pt
  175.   \centerline{(Version 4.0, 27th February 1991)}
  176.   \centerline{(Vax/VMS Version)}
  177.   \vfill}
  178. \def\botofcontents{\vfill
  179.   \centerline{\mc Acknowledgements}
  180.   \centerline{\hsize 5in\baselineskip9pt
  181.     \vbox{\ninerm\noindent
  182.     This program was originally based upon Flavio Rose's change file
  183.     which, applied to David Fuch's original \.{DVItype} program, generated
  184.     his \.{DVI2LNG} program.  (The output produced by \.{DVI2LNG} was then
  185.     further processed by \.{LN03TOPP} to yield a \.{LN3} file to drive a
  186.     Digital LN03 laser printer.)  Further extensions and enhancements were
  187.     added, and when the \.{WEB} change file grew to twice the size of the
  188.     original \.{DVITYPE.WEB} source, a manual \.{WEB}merge operation was
  189.     performed!\endgraf
  190.     Support for \.{\\changebar} specials was provided by Robin Fairbairns.
  191.     The code for the newer DEClaser printers is due to Karsten Nyblad.}}}
  192. \pageno=\contentspagenumber \advance\pageno by 1
  193. \let\maybe=\iftrue
  194. %\let\maybe=\iffalse
  195. \def\meta#1{{$\langle$\rm #1$\rangle$}}
  196. \def\9#1{}% Used with @@:sort entry}{printed entry@@>
  197.  
  198. @* Introduction.
  199. The \.{DVItoLN03} utility program reads binary device-independent (``\.{DVI}'')
  200. files that are produced by document compilers such as \TeX, and converts them
  201. into a form suited to driving a Digital LN03 or LN03-Plus laser printer.  The
  202. @^LN03 laser printer@>
  203. output file (with extension \.{LN3}) contains both the instructions to typeset
  204. @^LN3 file@>
  205. the required characters \&{and} the commands necessary to download the
  206. characters required (until DEC get around to providing the Computer Modern
  207. fonts in a ROM cartridge!).  The LN03-Plus will accept, without complaint,
  208. any number of downloaded fonts: however, only the first 14 fonts (each
  209. consisting of up to 188 characters, in the \\{GL} and \\{GR} sets) are
  210. actually usable for imaging glyphs: attempts to image a glyph from fonts
  211. |15..| result in the display of a character taken from one of the first 14
  212. fonts loaded.  In view of these limitations, \.{DVItoLN03} only downloads
  213. the characters which are actually used within the document being processed;
  214. the maximum possible number of characters, even if taken from different
  215. \TeX\ fonts, are combined into each single `LN03 font'.
  216.  
  217. The program was developed by Brian Hamilton Kelly at the Royal Military
  218. @^Hamilton Kelly, Brian@>
  219. College of Science.
  220. @^RMCS@>
  221. @^Royal Military College of Science@>
  222. The \.{DVI2LNG} program of Flavio Rose
  223. @^Rose, Flavio@>
  224. @:DVI2LNG program}{\.{DVI2LNG} program@>
  225. was used as a starting point; this had been distributed as a change file to
  226. be applied to the original \.{DVItype} program,
  227. @:DVItype Program}{\.{DVItype} program@>
  228. and ``translated'' the \.{DVI} file into two output files which were then
  229. further processed by his \.{LN03toPP}
  230. @:LN03toPP Program}{\.{LN03toPP} program@>
  231. program (written in PL/I), which required device-specific versions of
  232. unpacked pixel files.  The initial step was to perform the two separate
  233. passes within a single program (therefore the present program still needs to
  234. make two passes through the \.{DVI} file).  Later work added further
  235. functionality including use of ``ordinary'' pixel files  and support for
  236. \TeX's \.{\BS special} command to read sixel graphics dump files and include
  237. them verbatim within the output file.  More recent revisions have produced
  238. major versions 2, 3 and 4:
  239.  
  240. \yskip\hang\&{V2.0} Provided capability to read pixel rasters from packed
  241. (\.{PK}) files as well as from the traditional \.{PXL} files.  It also manages
  242. to handle the glyphs of  characters which are too large to load into the
  243. LN03's memory.
  244.  
  245. \yskip\hang\&{V3.0} Changed method of accessing font files, to allow users
  246. flexibility in the storage philosophy they adopt.  Can now handle large
  247. (256-character) fonts, such as Levy's Greek.
  248. @^Levy, Silvio@>
  249. @^256-character fonts@>
  250. @:two hundred and fifty six character fonts}{256-character fonts@>
  251.  
  252.  
  253. \yskip\hang\&{V3.1} Robin Fairbairns of Laser Scan (Cambridge, UK) added
  254. @^Fairbairns, Robin@>
  255. support for Rose's \.{\BS special}s for changebar marking at V3.1-1, June
  256. 1989.
  257.  
  258. \yskip\hang\&{V3.2} Tidied up recognition of \TeX\ \meta{physical unit}s.
  259. Removed final vestige of `hard-wired' logical names, \\{viz:} \.{TEX\$FONTS}
  260. @.TEX\$FONTS@>
  261.  
  262. \yskip\hang\&{V3.3} Permitted \.{/LEFT\UL MARGIN} and \.{TOP\UL MARGIN}
  263. qualifiers to take dimensioned lengths, instead of being restricted to
  264. pixels.
  265.  
  266. \yskip\hang\&{V3.4} Processed virtual font files, provided each character
  267. was only mapped to a single character from some other font; did not support
  268. virtual characters which mapped to a sequence of one or more characters from
  269. other physical fonts, nor could it handle font \.{recurse}.
  270.  
  271. \yskip\hang\&{V3.5} Handled sequences in virtual font files; still couldn't
  272. handle font \.{recurse}.
  273.  
  274. \yskip\hang\&{V3.6} Supports font \.{recurse}; folds multiple references to
  275. same font (name and scale) onto single invocation.
  276.  
  277. \yskip\hang\&{V3.7} Supports landscape and portrait specials; whole page only.
  278.  
  279. \yskip\hang\&{V3.8} Supports mixture of landscape and portrait on same page.
  280.  
  281. \yskip\hang\&{V3.9} Prints glyphs from missing fonts as solid rules; added
  282. \.{/HFUZZ} and \.{/VFUZZ} qualifiers.
  283.  
  284. \yskip\hang\&{V4.0} Supports the new DEClaser~2100 and~2200 printers (the
  285. LN05 and LN06) in addition to the traditional LN03.  With the LN06, paper
  286. feed trays may be selected from the command line, and the duplexing
  287. facilities controlled also.  Support for these printers was provided by
  288. Karsten Nyblad
  289. @^Nyblad, Karsten@>
  290. of TFL, the Danish Telecommunication Research Laboratory, and integrated
  291. into this new release with the other features mentioned above.
  292.  
  293. One of the stated {\it raisons d'\^^Detre\/} for \.{DVItype} is ``to serve as an
  294. example of a program that reads \.{DVI} files correctly, for system
  295. programmers who are developing \.{DVI}-related software.''   The paradigms for
  296. calculation provided by this example have been followed closely in the present
  297. program (by importing them {\it verbatim\/} from \.{DVItype}!)  To quote again
  298. from \.{DVItype}:\vskip 5pt
  299.  
  300.   \centerline{\hsize 5in\baselineskip9pt
  301.     \vbox{\ninerm\noindent
  302.       ``Programs for typesetting need to be especially careful about how they do
  303.       arithmetic; if rounding errors accumulate, margins won't be straight,
  304.       vertical rules won't line up, and so on. But if rounding is done
  305.       everywhere, even in the midst of words, there will be uneven spacing
  306.       between the letters, and that looks bad. Human eyes notice differences
  307.       of a thousandth of an inch in the positioning of lines that are close
  308.       together; on low resolution devices, where rounding produces effects
  309.       four times as great as this, the problem is especially critical.
  310.       Experience has shown that unusual care is needed even on high-resolution
  311.       equipment; for example, a mistake in the sixth significant hexadecimal
  312.       place of a constant once led to a difficult-to-find bug in some software
  313.       for the Alphatype CRS, which has a resolution of 5333 pixels per inch
  314.       (make that 5333.33333333 pixels per inch).  The document compilers that
  315.       generate \.{DVI} files make certain assumptions about the arithmetic
  316.       that will be used by \.{DVI}-reading software, and if these assumptions
  317.       are violated the results will be of inferior quality.''
  318.    }}
  319.  
  320. The first \.{DVItype} program was designed by David Fuchs in 1979, and it
  321. @^Fuchs, David Raymond@>
  322. went through several versions on different computers as the format of
  323. \.{DVI} files was evolving to its present form.
  324.  
  325. The \.{LN3} output file produced by DVItoLN03 always commences by including
  326. commands to down-load one or more font-files (each of a maximum of 188
  327. characters) to define just those characters used in the document. The rest of
  328. the file consists of commands to set those characters where required.  Because
  329. the mapping of source characters to those in the down-loaded fonts is
  330. arbitrary, the output file is unintelligible to human readers!
  331.  
  332. Whilst many of the earlier changes tried to preserve the
  333. ``machine-independence'' of the program, no attempt has been made in this
  334. version, since it is assumed that the LN03 printer will be used on a VAX
  335. computer.  In particular, |random_reading| is used on some of the input files,
  336. and is necessary.
  337.  
  338. The |banner| string defined here should be changed whenever \.{DVItoLN03}
  339. gets modified.
  340.  
  341. @d banner=='This is DVItoLN03, Vax/VMS Version 4.0'
  342.  
  343. @ This program is written mostly in standard \PASCAL, except where it is
  344. necessary to use extensions; for example, \.{DVItoLN03} must read files whose
  345. names are dynamically specified, and that would be impossible in pure \PASCAL.
  346. Many features of VAX-\PASCAL\ have been incorporated, and no attempt has been
  347. made to ensure that all places where such nonstandard constructions are used
  348. have been listed in the index under ``system dependencies.''
  349. @!@^system dependencies@>
  350.  
  351. One of the extensions to standard \PASCAL\ that we shall deal with is the
  352. ability to move to a random place in a binary file; another is to
  353. determine the length of a binary file. Such extensions are not necessary
  354. for reading \.{DVI} files, and they are not important for efficiency
  355. reasons either.  However, when creating the `font files' for
  356. downloading the necessary characters to the LN03, \.{DVItoLN03} \&{has} to be
  357. able to perform |random_reading|, to locate the glyph of each character within
  358. the pixel file.  Therefore, if \.{DVItoLN03} is being used with
  359. \PASCAL s for which random file positioning is not efficiently available,
  360. and the following definition is changed from |true| to |false|, further work
  361. will still be required to re-code the processing of the pixel files.
  362.  
  363. Another extension is to use a default |case| as in \.{TANGLE}, \.{WEAVE},
  364. etc.
  365.  
  366.  
  367. @d random_reading==true {should we skip around in the file?}
  368. @d othercases == otherwise {Vax/VMS default for cases not listed explicitly}
  369. @d endcases == @+end {follows the default case in an extended |case| statement}
  370. @f othercases == else
  371. @f endcases == end
  372.  
  373. @ The binary input comes from |dvi_file|, and the \.{LN3} file is written to
  374. |ln3_file|.  \.{DVItoLN03} also produces a `log' file using |type_file|.
  375. Errors and some informational messages are written to this, and are also
  376. written to \PASCAL's standard |output| file.  The term |print| is used instead
  377. of |write| when this program writes on |type_file|, so that all such output
  378. could easily be redirected if desired.
  379.  
  380. Under Vax/VMS we use double-precision for all real values.  Furthermore, since
  381. extensive reference is made to Vax/VMS system services and other library
  382. routines, we arrange for the \PASCAL\ program to `inherit' all their
  383. definitions (from \.{STARLET}).
  384.  
  385. @d print(#)==write(type_file,#)
  386. @d print_ln(#)==write_ln(type_file,#)
  387. @d real==double
  388.  
  389. @p @=[INHERIT ('SYS$LIBRARY:STARLET')]@> program DVI_to_LN03(@!dvi_file,
  390.                                 @!tfm_file,@!type_file,@!output,@!ln3_file);
  391. label @<Labels in the outer block@>@/
  392. const @<Constants in the outer block@>@/
  393. type @<Types in the outer block@>@/
  394. var @<Globals in the outer block@>@/
  395. procedure jump_out; forward;@#
  396. @t\2@>@<Procedures for initialization@>@;@#
  397. procedure initialize; {this procedure gets things started properly}
  398.   var @!i:integer; {loop index for initializations}
  399.   begin
  400.   @<Preset initial values@>@/
  401.   print_ln(banner);@/
  402.   @<Set initial values@>@/
  403.   end;@#
  404. @<Additional low-level procedures@>@;
  405.  
  406. @ If the program has to stop prematurely, it goes to the
  407. `|final_end|'. Another label, |done|, is used when stopping normally.
  408.  
  409. @d final_end=9999 {label for the end of it all}
  410. @d done=30 {go here when finished with a subtask}
  411.  
  412. @<Labels...@>=final_end,done;
  413.  
  414. @ The following parameters can be changed at compile time to extend or
  415. reduce \.{DVItoLN03}'s capacity.
  416.  
  417. As previously mentioned, the LN03-plus is quite happy to accept any number
  418. of downloaded fonts, but is incapable of imaging glyphs from more than
  419. fourteen of them!  (The basic LN03 won't accept more than 21 half-fonts.)
  420. Each such font has to be selected by mapping it to one of 10 different
  421. Select Graphic Renditions; this is reflected by the constant |max_SGR|.
  422.  
  423. Whenever bitmaps are sent to the printer (in sixel format), it is necessary
  424. to establish the ``printing'' position 29 pixels  above the current
  425. reference point. Despite repeated enquiry of DEC, it has not been
  426. established why this is necessary. Naturally, we call this 29 pixels
  427. |bitmap_offset|.  This same offset also has to be applied to sixel graphics
  428. included through a \.{\\special} command.
  429.  
  430. When the paper orientation is selected, we may define the |page_len| and
  431. |page_wid| in terms of the physical limits of the paper; these sizes are
  432. as follows (for European A4 paper).
  433. @^European A4 paper@>
  434. (These might later become variables, and be alterable by qualifiers in the
  435. command line.)  There is, however, a further complication.  In either printing
  436. orientation, the printable area is of these dimensions; however, this printing
  437. area starts 0.25~inches from the top left corner of the paper \\{in the
  438. orientation it enters the printer}.  Therefore in portrait mode, we can
  439. actually print from (75,75) to (2475,3475), with (300,300) being the
  440. ``normal'' \TeX\ origin, but in landscape mode we can print (35,75) to
  441. (3435,2475), and \TeX's origin should still be at (300,300).
  442.  
  443. The LN03 has a physical (apparently arbitrary) restriction on the maximum size
  444. of a downloadable glyph.  For a glyph to be acceptable, it must satisfy the
  445. inequality $$
  446.   2\times\lceil|num_cols|/2\rceil\times\lceil|num_rows|/8\rceil\le5700$$
  447.  
  448. The type |file_spec| is used for declaring data objects containing VAX/RMS
  449. file specifications, and for declaring procedures which handle them.
  450.  
  451. @d max_line=200         {Maximum length of any line output to the LN03}
  452. @d max_print=175        {Maximum number of characters imaged on line}
  453. @#
  454. @d max_lnfonts=13       {Limitations of the LN03's hardware}
  455. @d max_SGR=9
  456. @#
  457. @d max_special=300 {maximum size of a \.{\\special} command that we can deal
  458. with}
  459. @d max_points=256 {number of points for the \.{\\special} rule commands}
  460. @d max_strings=30       {Distinct string parameters of \.{\\special}s}
  461. @d pool_size=100        {Total characters in those strings}
  462. @#
  463. @d bitmap_offset=29
  464. @#
  465. @d paper_ht=3400                {in pixels}
  466. @d paper_wid=2400
  467. @d y_port_min=75
  468. @d y_land_min=35
  469. @d page_x_min=75
  470. @d y_top=75       {Minimum addressable $y$ pixel}
  471. @d y_bot=paper_ht+y_port_min {Maximum addressable $y$ pixel}
  472. @#
  473. @d PFS_landscape=='?23 J' {Extended A4 paper in landscape orientation}
  474. @d PFS_portrait=='?22 J'  {ditto in portrait; each preceded by |csi|}
  475. @#
  476. @d lnf_bufsize=2000     {Maximum block size of an LN03 ``font file'' in memory ($\approx1$MB)}
  477. @#
  478. @d largest_glyph=5700
  479. @#
  480. @d max_fonts=100 {maximum number of distinct fonts per \.{DVI} file}
  481. @d max_glyphs=10000 {maximum number of different characters among all fonts}
  482. @#
  483. @d line_length=79 {bracketed lines of output will be at most this long}
  484. @d terminal_line_length=150 {maximum number of characters input in a single
  485.   line of input from the command line interpreter}
  486. @#
  487. @d stack_size=100 {\.{DVI} files shouldn't |push| beyond this depth}
  488. @d name_size=1000 {total length of all font file names}
  489. @#
  490. @d name_length=255 {a file name shouldn't be longer than this}
  491. @#
  492. @d max_rules=16384 {maximum number of rules per page}
  493. @d max_lap_rules=3 {maximum under/over lap of rules}
  494. @#
  495. @d vf_size=10000 {maximum |dvi| commands stored from virtual font files}
  496. @#
  497. @d max_blank_pages=100 {blank \\{verso} pages between successive \\{recto}s}
  498.  
  499. @<Types...@>=
  500. @!file_spec = varying [name_length] of char;
  501.  
  502. @ Here are some macros for common programming idioms.
  503.  
  504. @d incr(#) == #:=#+1 {increase a variable by unity}
  505. @d decr(#) == #:=#-1 {decrease a variable by unity}
  506. @d do_nothing == {empty statement}
  507.  
  508. @ If the \.{DVI} file is badly malformed, the whole process must be aborted;
  509. \.{DVItoLN03} will give up, after issuing an error message about the symptoms
  510. that were noticed.
  511.  
  512. Such errors might be discovered inside of subroutines inside of subroutines,
  513. so a procedure called |jump_out| has been introduced. This procedure, which
  514. simply transfers control to the label |final_end| at the end of the program,
  515. contains the only non-local |goto| statement in \.{DVItoLN03}.
  516. @^system dependencies@>
  517.  
  518. @d crlf==chr(13)+chr(10)
  519. @d abort(#)==begin
  520.         history := fatal_error; print_ln(' ');
  521.         print_ln(' Fatal error: ',#);
  522. @!@.Fatal error ...@>
  523.         write_ln(output,crlf,'Fatal error: ',#,crlf);
  524.         jump_out;
  525.     end
  526. @d bad_dvi(#)==abort('Bad DVI file: ',#,'!')
  527. @!@.Bad DVI file@>
  528. @!@:fatal error Bad DVI file}{\quad\.{Bad DVI file} \\{(q.v.)}@>
  529. @d capacity_exceeded(#)==abort('capacity exceeded [',#,']')
  530. @!@.capacity exceeded@>
  531. @!@:fatal error capacity exceeded}{\quad\.{capacity exceeded} \\{(q.v.)}@>
  532.  
  533. @p procedure jump_out;
  534. begin goto final_end;
  535. end;
  536.  
  537. @* The character set.
  538. Like all programs written with the  \.{WEB} system, \.{DVItoLN03} can be
  539. used with any character set. But it uses ASCII code internally, because
  540. the programming for portable input-output is easier when a fixed internal
  541. code is used, and because \.{DVI} files use ASCII code for file names
  542. and certain other strings.
  543.  
  544. The next few sections of \.{DVItoLN03} have therefore been copied from the
  545. analogous ones in the \.{WEB} system routines. They have been considerably
  546. simplified, since \.{DVItoLN03} need not deal with the controversial
  547. ASCII codes less than @'40. If such codes appear in the \.{DVI} file,
  548. they will be printed as question marks.
  549.  
  550. All characters still occupy 8 bits of storage, to avoid overhead with |packed
  551. array|s.
  552.  
  553. @<Types...@>=
  554. @!ASCII_code= @=[byte]@> " ".."~"; {a subrange of the integers}
  555.  
  556. @ The original \PASCAL\ compiler was designed in the late 60s, when six-bit
  557. character sets were common, so it did not make provision for lower case
  558. letters. Nowadays, of course, we need to deal with both upper and lower case
  559. alphabets in a convenient way, especially in a program like \.{DVItoLN03}.
  560. So we shall assume that the \PASCAL\ system being used for \.{DVItoLN03}
  561. has a character set containing at least the standard visible characters
  562. of ASCII code (|"!"| through |"~"|).
  563.  
  564. Some \PASCAL\ compilers use the original name |char| for the data type
  565. associated with the characters in text files, while other \PASCAL s
  566. consider |char| to be a 64-element subrange of a larger data type that has
  567. some other name.  In order to accommodate this difference, we shall use
  568. the name |text_char| to stand for the data type of the characters in the
  569. output file.  We shall also assume that |text_char| consists of
  570. the elements |chr(first_text_char)| through |chr(last_text_char)|,
  571. inclusive. The following definitions should be adjusted if necessary.
  572. @^system dependencies@>
  573.  
  574. @d text_char == char {the data type of characters in text files}
  575. @d first_text_char=0 {ordinal number of the smallest element of |text_char|}
  576. @d last_text_char=255 {ordinal number of the largest element of |text_char|}
  577.  
  578. @<Types...@>=
  579. @!text_file=packed file of text_char;
  580.  
  581. @ The \.{DVItoLN03} processor converts between ASCII code and
  582. the user's external character set by means of arrays |xord| and |xchr|
  583. that are analogous to \PASCAL's |ord| and |chr| functions.
  584.  
  585. @<Globals...@>=
  586. @!xord: array [text_char] of ASCII_code;
  587.   {specifies conversion of input characters}
  588. @!xchr: array [first_text_char..last_text_char] of text_char;
  589.   {specifies conversion of output characters}
  590.  
  591. @ Under our assumption that the visible characters of standard ASCII are
  592. all present, the following assignment statements initialize the
  593. |xchr| array properly, without needing any system-dependent changes.
  594.  
  595. @<Set init...@>=
  596. for i:=0 to @'37 do xchr[i]:='?';
  597. xchr[@'40]:=' ';
  598. xchr[@'41]:='!';
  599. xchr[@'42]:='"';
  600. xchr[@'43]:='#';
  601. xchr[@'44]:='$';
  602. xchr[@'45]:='%';
  603. xchr[@'46]:='&';
  604. xchr[@'47]:='''';@/
  605. xchr[@'50]:='(';
  606. xchr[@'51]:=')';
  607. xchr[@'52]:='*';
  608. xchr[@'53]:='+';
  609. xchr[@'54]:=',';
  610. xchr[@'55]:='-';
  611. xchr[@'56]:='.';
  612. xchr[@'57]:='/';@/
  613. xchr[@'60]:='0';
  614. xchr[@'61]:='1';
  615. xchr[@'62]:='2';
  616. xchr[@'63]:='3';
  617. xchr[@'64]:='4';
  618. xchr[@'65]:='5';
  619. xchr[@'66]:='6';
  620. xchr[@'67]:='7';@/
  621. xchr[@'70]:='8';
  622. xchr[@'71]:='9';
  623. xchr[@'72]:=':';
  624. xchr[@'73]:=';';
  625. xchr[@'74]:='<';
  626. xchr[@'75]:='=';
  627. xchr[@'76]:='>';
  628. xchr[@'77]:='?';@/
  629. xchr[@'100]:='@@';
  630. xchr[@'101]:='A';
  631. xchr[@'102]:='B';
  632. xchr[@'103]:='C';
  633. xchr[@'104]:='D';
  634. xchr[@'105]:='E';
  635. xchr[@'106]:='F';
  636. xchr[@'107]:='G';@/
  637. xchr[@'110]:='H';
  638. xchr[@'111]:='I';
  639. xchr[@'112]:='J';
  640. xchr[@'113]:='K';
  641. xchr[@'114]:='L';
  642. xchr[@'115]:='M';
  643. xchr[@'116]:='N';
  644. xchr[@'117]:='O';@/
  645. xchr[@'120]:='P';
  646. xchr[@'121]:='Q';
  647. xchr[@'122]:='R';
  648. xchr[@'123]:='S';
  649. xchr[@'124]:='T';
  650. xchr[@'125]:='U';
  651. xchr[@'126]:='V';
  652. xchr[@'127]:='W';@/
  653. xchr[@'130]:='X';
  654. xchr[@'131]:='Y';
  655. xchr[@'132]:='Z';
  656. xchr[@'133]:='[';
  657. xchr[@'134]:='\';
  658. xchr[@'135]:=']';
  659. xchr[@'136]:='^';
  660. xchr[@'137]:='_';@/
  661. xchr[@'140]:='`';
  662. xchr[@'141]:='a';
  663. xchr[@'142]:='b';
  664. xchr[@'143]:='c';
  665. xchr[@'144]:='d';
  666. xchr[@'145]:='e';
  667. xchr[@'146]:='f';
  668. xchr[@'147]:='g';@/
  669. xchr[@'150]:='h';
  670. xchr[@'151]:='i';
  671. xchr[@'152]:='j';
  672. xchr[@'153]:='k';
  673. xchr[@'154]:='l';
  674. xchr[@'155]:='m';
  675. xchr[@'156]:='n';
  676. xchr[@'157]:='o';@/
  677. xchr[@'160]:='p';
  678. xchr[@'161]:='q';
  679. xchr[@'162]:='r';
  680. xchr[@'163]:='s';
  681. xchr[@'164]:='t';
  682. xchr[@'165]:='u';
  683. xchr[@'166]:='v';
  684. xchr[@'167]:='w';@/
  685. xchr[@'170]:='x';
  686. xchr[@'171]:='y';
  687. xchr[@'172]:='z';
  688. xchr[@'173]:='{';
  689. xchr[@'174]:='|';
  690. xchr[@'175]:='}';
  691. xchr[@'176]:='~';
  692. for i:=@'177 to 255 do xchr[i]:='?';
  693.  
  694. @ The following system-independent code makes the |xord| array contain a
  695. suitable inverse to the information in |xchr|.
  696.  
  697. @<Set init...@>=
  698. for i:=first_text_char to last_text_char do xord[chr(i)]:=@'40;
  699. for i:=" " to "~" do xord[xchr[i]]:=i;
  700.  
  701. @* Device-independent file format.
  702. Before we get into the details of \.{DVItoLN03}, we need to know exactly
  703. what \.{DVI} files are. The form of such files was designed by David R.
  704. @^Fuchs, David Raymond@>
  705. Fuchs in 1979. Almost any reasonable typesetting device can be driven by
  706. a program that takes \.{DVI} files as input, and dozens of such
  707. \.{DVI}-to-whatever programs have been written. Thus, it is possible to
  708. print the output of document compilers like \TeX\ on many different kinds
  709. of equipment.
  710.  
  711. A \.{DVI} file is a stream of 8-bit bytes, which may be regarded as a
  712. series of commands in a machine-like language. The first byte of each command
  713. is the operation code, and this code is followed by zero or more bytes
  714. that provide parameters to the command. The parameters themselves may consist
  715. of several consecutive bytes; for example, the `|set_rule|' command has two
  716. parameters, each of which is four bytes long. Parameters are usually
  717. regarded as nonnegative integers; but four-byte-long parameters,
  718. and shorter parameters that denote distances, can be
  719. either positive or negative. Such parameters are given in two's complement
  720. notation. For example, a two-byte-long distance parameter has a value between
  721. $-2^{15}$ and $2^{15}-1$.
  722. @.DVI {\rm files}@>
  723.  
  724. A \.{DVI} file consists of a ``preamble,'' followed by a sequence of one
  725. or more ``pages,'' followed by a ``postamble.'' The preamble is simply a
  726. |pre| command, with its parameters that define the dimensions used in the
  727. file; this must come first.  Each ``page'' consists of a |bop| command,
  728. followed by any number of other commands that tell where characters are to
  729. be placed on a physical page, followed by an |eop| command. The pages
  730. appear in the order that they were generated, not in any particular
  731. numerical order. If we ignore |nop| commands and \\{fnt\_def} commands
  732. (which are allowed between any two commands in the file), each |eop|
  733. command is immediately followed by a |bop| command, or by a |post|
  734. command; in the latter case, there are no more pages in the file, and the
  735. remaining bytes form the postamble.  Further details about the postamble
  736. will be explained later.
  737.  
  738. Some parameters in \.{DVI} commands are ``pointers.'' These are four-byte
  739. quantities that give the location number of some other byte in the file;
  740. the first byte is number~0, then comes number~1, and so on. For example,
  741. one of the parameters of a |bop| command points to the previous |bop|;
  742. this makes it feasible to read the pages in backwards order, in case the
  743. results are being directed to a device that stacks its output face up.
  744. Suppose the preamble of a \.{DVI} file occupies bytes 0 to 99. Now if the
  745. first page occupies bytes 100 to 999, say, and if the second
  746. page occupies bytes 1000 to 1999, then the |bop| that starts in byte 1000
  747. points to 100 and the |bop| that starts in byte 2000 points to 1000. (The
  748. very first |bop|, i.e., the one that starts in byte 100, has a pointer of $-1$.)
  749.  
  750. @<Glob...@>=
  751. @!dvi_file:byte_file; {the stuff we are \.{DVItoLN03}ing}
  752.  
  753. @ The \.{DVI} format is intended to be both compact and easily interpreted
  754. by a machine. Compactness is achieved by making most of the information
  755. implicit instead of explicit. When a \.{DVI}-reading program reads the
  756. commands for a page, it keeps track of several quantities: (a)~The current
  757. font |f| is an integer; this value is changed only
  758. by \\{fnt} and \\{fnt\_num} commands. (b)~The current position on the page
  759. is given by two numbers called the horizontal and vertical coordinates,
  760. |h| and |v|. Both coordinates are zero at the upper left corner of the page;
  761. moving to the right corresponds to increasing the horizontal coordinate, and
  762. moving down corresponds to increasing the vertical coordinate. Thus, the
  763. coordinates are essentially Cartesian, except that vertical directions are
  764. flipped; the Cartesian version of |(h,v)| would be |(h,-v)|.  (c)~The
  765. current spacing amounts are given by four numbers |w|, |x|, |y|, and |z|,
  766. where |w| and~|x| are used for horizontal spacing and where |y| and~|z|
  767. are used for vertical spacing. (d)~There is a stack containing
  768. |(h,v,w,x,y,z)| values; the \.{DVI} commands |push| and |pop| are used to
  769. change the current level of operation. Note that the current font~|f| is
  770. not pushed and popped; the stack contains only information about
  771. positioning.
  772.  
  773. The values of |h|, |v|, |w|, |x|, |y|, and |z| are signed integers having up
  774. to 32 bits, including the sign. Since they represent physical distances,
  775. there is a small unit of measurement such that increasing |h| by~1 means
  776. moving a certain tiny distance to the right. The actual unit of
  777. measurement is variable, as explained below.
  778.  
  779. @ Here is a list of all the commands that may appear in a \.{DVI} file. Each
  780. command is specified by its symbolic name (e.g., |bop|), its opcode byte
  781. (e.g., 139), and its parameters (if any). The parameters are followed
  782. by a bracketed number telling how many bytes they occupy; for example,
  783. `|p[4]|' means that parameter |p| is four bytes long.
  784.  
  785. \yskip\hang|set_char_0| 0. Typeset character number~0 from font~|f|
  786. such that the reference point of the character is at |(h,v)|. Then
  787. increase |h| by the width of that character. Note that a character may
  788. have zero or negative width, so one cannot be sure that |h| will advance
  789. after this command; but |h| usually does increase.
  790.  
  791. \yskip\hang|set_char_1| through |set_char_127| (opcodes 1 to 127).
  792. Do the operations of |set_char_0|; but use the character whose number
  793. matches the opcode, instead of character~0.
  794.  
  795. \yskip\hang|set1| 128 |c[1]|. Same as |set_char_0|, except that character
  796. number~|c| is typeset. \TeX82 uses this command for characters in the
  797. range |128<=c<256|.
  798.  
  799. \yskip\hang|set2| 129 |c[2]|. Same as |set1|, except that |c|~is two
  800. bytes long, so it is in the range |0<=c<65536|. \TeX82 never uses this
  801. command, which is intended for processors that deal with oriental languages;
  802. but \.{DVItoLN03} will allow character codes greater than 255, assuming that
  803. they all have the same width as the character whose code is $c \bmod 256$.
  804. @^oriental characters@>@^Chinese characters@>@^Japanese characters@>
  805.  
  806. \yskip\hang|set3| 130 |c[3]|. Same as |set1|, except that |c|~is three
  807. bytes long, so it can be as large as $2^{24}-1$.
  808.  
  809. \yskip\hang|set4| 131 |c[4]|. Same as |set1|, except that |c|~is four
  810. bytes long, possibly even negative. Imagine that.
  811.  
  812. \yskip\hang|set_rule| 132 |a[4]| |b[4]|. Typeset a solid black rectangle
  813. of height |a| and width |b|, with its bottom left corner at |(h,v)|. Then
  814. set |h:=h+b|. If either |a<=0| or |b<=0|, nothing should be typeset. Note
  815. that if |b<0|, the value of |h| will decrease even though nothing else happens.
  816. Programs that typeset from \.{DVI} files should be careful to make the rules
  817. line up carefully with digitized characters, as explained in connection with
  818. the |rule_pixels| subroutine below.
  819.  
  820. \yskip\hang|put1| 133 |c[1]|. Typeset character number~|c| from font~|f|
  821. such that the reference point of the character is at |(h,v)|. (The `put'
  822. commands are exactly like the `set' commands, except that they simply put out a
  823. character or a rule without moving the reference point afterwards.)
  824.  
  825. \yskip\hang|put2| 134 |c[2]|. Same as |set2|, except that |h| is not changed.
  826.  
  827. \yskip\hang|put3| 135 |c[3]|. Same as |set3|, except that |h| is not changed.
  828.  
  829. \yskip\hang|put4| 136 |c[4]|. Same as |set4|, except that |h| is not changed.
  830.  
  831. \yskip\hang|put_rule| 137 |a[4]| |b[4]|. Same as |set_rule|, except that
  832. |h| is not changed.
  833.  
  834. \yskip\hang|nop| 138. No operation, do nothing. Any number of |nop|'s
  835. may occur between \.{DVI} commands, but a |nop| cannot be inserted between
  836. a command and its parameters or between two parameters.
  837.  
  838. \yskip\hang|bop| 139 $c_0[4]$ $c_1[4]$ $\ldots$ $c_9[4]$ $p[4]$. Beginning
  839. of a page: Set |(h,v,w,x,y,z):=(0,0,0,0,0,0)| and set the stack empty. Set
  840. the current font |f| to an undefined value.  The ten $c_i$ parameters can
  841. be used to identify pages, if a user wants to print only part of a \.{DVI}
  842. file; \TeX82 gives them the values of \.{\\count0} $\ldots$ \.{\\count9}
  843. at the time \.{\\shipout} was invoked for this page.  The parameter |p|
  844. points to the previous |bop| command in the file, where the first |bop|
  845. has $p=-1$.
  846.  
  847. \yskip\hang|eop| 140.  End of page: Print what you have read since the
  848. previous |bop|. At this point the stack should be empty. (The \.{DVI}-reading
  849. programs that drive most output devices will have kept a buffer of the
  850. material that appears on the page that has just ended. This material is
  851. largely, but not entirely, in order by |v| coordinate and (for fixed |v|) by
  852. |h|~coordinate; so it usually needs to be sorted into some order that is
  853. appropriate for the device in question. \.{DVItoLN03} does do such sorting.)
  854.  
  855. \yskip\hang|push| 141. Push the current values of |(h,v,w,x,y,z)| onto the
  856. top of the stack; do not change any of these values. Note that |f| is
  857. not pushed.
  858.  
  859. \yskip\hang|pop| 142. Pop the top six values off of the stack and assign
  860. them to |(h,v,w,x,y,z)|. The number of pops should never exceed the number
  861. of pushes, since it would be highly embarrassing if the stack were empty
  862. at the time of a |pop| command.
  863.  
  864. \yskip\hang|right1| 143 |b[1]|. Set |h:=h+b|, i.e., move right |b| units.
  865. The parameter is a signed number in two's complement notation, |-128<=b<128|;
  866. if |b<0|, the reference point actually moves left.
  867.  
  868. \yskip\hang|right2| 144 |b[2]|. Same as |right1|, except that |b| is a
  869. two-byte quantity in the range |-32768<=b<32768|.
  870.  
  871. \yskip\hang|right3| 145 |b[3]|. Same as |right1|, except that |b| is a
  872. three-byte quantity in the range |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
  873.  
  874. \yskip\hang|right4| 146 |b[4]|. Same as |right1|, except that |b| is a
  875. four-byte quantity in the range |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
  876.  
  877. \yskip\hang|w0| 147. Set |h:=h+w|; i.e., move right |w| units. With luck,
  878. this parameterless command will usually suffice, because the same kind of motion
  879. will occur several times in succession; the following commands explain how
  880. |w| gets particular values.
  881.  
  882. \yskip\hang|w1| 148 |b[1]|. Set |w:=b| and |h:=h+b|. The value of |b| is a
  883. signed quantity in two's complement notation, |-128<=b<128|. This command
  884. changes the current |w|~spacing and moves right by |b|.
  885.  
  886. \yskip\hang|w2| 149 |b[2]|. Same as |w1|, but |b| is a two-byte-long
  887. parameter, |-32768<=b<32768|.
  888.  
  889. \yskip\hang|w3| 150 |b[3]|. Same as |w1|, but |b| is a three-byte-long
  890. parameter, |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
  891.  
  892. \yskip\hang|w4| 151 |b[4]|. Same as |w1|, but |b| is a four-byte-long
  893. parameter, |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
  894.  
  895. \yskip\hang|x0| 152. Set |h:=h+x|; i.e., move right |x| units. The `|x|'
  896. commands are like the `|w|' commands except that they involve |x| instead
  897. of |w|.
  898.  
  899. \yskip\hang|x1| 153 |b[1]|. Set |x:=b| and |h:=h+b|. The value of |b| is a
  900. signed quantity in two's complement notation, |-128<=b<128|. This command
  901. changes the current |x|~spacing and moves right by |b|.
  902.  
  903. \yskip\hang|x2| 154 |b[2]|. Same as |x1|, but |b| is a two-byte-long
  904. parameter, |-32768<=b<32768|.
  905.  
  906. \yskip\hang|x3| 155 |b[3]|. Same as |x1|, but |b| is a three-byte-long
  907. parameter, |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
  908.  
  909. \yskip\hang|x4| 156 |b[4]|. Same as |x1|, but |b| is a four-byte-long
  910. parameter, |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
  911.  
  912. \yskip\hang|down1| 157 |a[1]|. Set |v:=v+a|, i.e., move down |a| units.
  913. The parameter is a signed number in two's complement notation, |-128<=a<128|;
  914. if |a<0|, the reference point actually moves up.
  915.  
  916. \yskip\hang|down2| 158 |a[2]|. Same as |down1|, except that |a| is a
  917. two-byte quantity in the range |-32768<=a<32768|.
  918.  
  919. \yskip\hang|down3| 159 |a[3]|. Same as |down1|, except that |a| is a
  920. three-byte quantity in the range |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
  921.  
  922. \yskip\hang|down4| 160 |a[4]|. Same as |down1|, except that |a| is a
  923. four-byte quantity in the range |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
  924.  
  925. \yskip\hang|y0| 161. Set |v:=v+y|; i.e., move down |y| units. With luck,
  926. this parameterless command will usually suffice, because the same kind of motion
  927. will occur several times in succession; the following commands explain how
  928. |y| gets particular values.
  929.  
  930. \yskip\hang|y1| 162 |a[1]|. Set |y:=a| and |v:=v+a|. The value of |a| is a
  931. signed quantity in two's complement notation, |-128<=a<128|. This command
  932. changes the current |y|~spacing and moves down by |a|.
  933.  
  934. \yskip\hang|y2| 163 |a[2]|. Same as |y1|, but |a| is a two-byte-long
  935. parameter, |-32768<=a<32768|.
  936.  
  937. \yskip\hang|y3| 164 |a[3]|. Same as |y1|, but |a| is a three-byte-long
  938. parameter, |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
  939.  
  940. \yskip\hang|y4| 165 |a[4]|. Same as |y1|, but |a| is a four-byte-long
  941. parameter, |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
  942.  
  943. \yskip\hang|z0| 166. Set |v:=v+z|; i.e., move down |z| units. The `|z|' commands
  944. are like the `|y|' commands except that they involve |z| instead of |y|.
  945.  
  946. \yskip\hang|z1| 167 |a[1]|. Set |z:=a| and |v:=v+a|. The value of |a| is a
  947. signed quantity in two's complement notation, |-128<=a<128|. This command
  948. changes the current |z|~spacing and moves down by |a|.
  949.  
  950. \yskip\hang|z2| 168 |a[2]|. Same as |z1|, but |a| is a two-byte-long
  951. parameter, |-32768<=a<32768|.
  952.  
  953. \yskip\hang|z3| 169 |a[3]|. Same as |z1|, but |a| is a three-byte-long
  954. parameter, |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
  955.  
  956. \yskip\hang|z4| 170 |a[4]|. Same as |z1|, but |a| is a four-byte-long
  957. parameter, |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
  958.  
  959. \yskip\hang|fnt_num_0| 171. Set |f:=0|. Font 0 must previously have been
  960. defined by a \\{fnt\_def} instruction, as explained below.
  961.  
  962. \yskip\hang|fnt_num_1| through |fnt_num_63| (opcodes 172 to 234). Set
  963. |f:=1|, \dots, |f:=63|, respectively.
  964.  
  965. \yskip\hang|fnt1| 235 |k[1]|. Set |f:=k|. \TeX82 uses this command for font
  966. numbers in the range |64<=k<256|.
  967.  
  968. \yskip\hang|fnt2| 236 |k[2]|. Same as |fnt1|, except that |k|~is two
  969. bytes long, so it is in the range |0<=k<65536|. \TeX82 never generates this
  970. command, but large font numbers may prove useful for specifications of
  971. color or texture, or they may be used for special fonts that have fixed
  972. numbers in some external coding scheme.
  973.  
  974. \yskip\hang|fnt3| 237 |k[3]|. Same as |fnt1|, except that |k|~is three
  975. bytes long, so it can be as large as $2^{24}-1$.
  976.  
  977. \yskip\hang|fnt4| 238 |k[4]|. Same as |fnt1|, except that |k|~is four
  978. bytes long; this is for the really big font numbers (and for the negative ones).
  979.  
  980. \yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in
  981. general; it functions as a $(k+2)$-byte |nop| unless special \.{DVI}-reading
  982. programs are being used. \TeX82 generates |xxx1| when a short enough
  983. \.{\\special} appears, setting |k| to the number of bytes being sent. It
  984. is recommended that |x| be a string having the form of a keyword followed
  985. by possible parameters relevant to that keyword.
  986.  
  987. \yskip\hang|xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|.
  988.  
  989. \yskip\hang|xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|.
  990.  
  991. \yskip\hang|xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be ridiculously
  992. large. \TeX82 uses |xxx4| when |xxx1| would be incorrect.
  993.  
  994. \yskip\hang|fnt_def1| 243 |k[1]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
  995. Define font |k|, where |0<=k<256|; font definitions will be explained shortly.
  996.  
  997. \yskip\hang|fnt_def2| 244 |k[2]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
  998. Define font |k|, where |0<=k<65536|.
  999.  
  1000. \yskip\hang|fnt_def3| 245 |k[3]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
  1001. Define font |k|, where |0<=k<@t$2^{24}$@>|.
  1002.  
  1003. \yskip\hang|fnt_def4| 246 |k[4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
  1004. Define font |k|, where |@t$-2^{31}$@><=k<@t$2^{31}$@>|.
  1005.  
  1006. \yskip\hang|pre| 247 |i[1]| |num[4]| |den[4]| |mag[4]| |k[1]| |x[k]|.
  1007. Beginning of the preamble; this must come at the very beginning of the
  1008. file. Parameters |i|, |num|, |den|, |mag|, |k|, and |x| are explained below.
  1009.  
  1010. \yskip\hang|post| 248. Beginning of the postamble, see below.
  1011.  
  1012. \yskip\hang|post_post| 249. Ending of the postamble, see below.
  1013.  
  1014. \yskip\noindent Commands 250--255 are undefined at the present time.
  1015.  
  1016. @ @d set_char_0=0 {typeset character 0 and move right}
  1017. @d set1=128 {typeset a character and move right}
  1018. @d set_rule=132 {typeset a rule and move right}
  1019. @d put1=133 {typeset a character}
  1020. @d put_rule=137 {typeset a rule}
  1021. @d nop=138 {no operation}
  1022. @d bop=139 {beginning of page}
  1023. @d eop=140 {ending of page}
  1024. @d push=141 {save the current positions}
  1025. @d pop=142 {restore previous positions}
  1026. @d right1=143 {move right}
  1027. @d w0=147 {move right by |w|}
  1028. @d w1=148 {move right and set |w|}
  1029. @d x0=152 {move right by |x|}
  1030. @d x1=153 {move right and set |x|}
  1031. @d down1=157 {move down}
  1032. @d y0=161 {move down by |y|}
  1033. @d y1=162 {move down and set |y|}
  1034. @d z0=166 {move down by |z|}
  1035. @d z1=167 {move down and set |z|}
  1036. @d fnt_num_0=171 {set current font to 0}
  1037. @d fnt1=235 {set current font}
  1038. @d xxx1=239 {extension to \.{DVI} primitives}
  1039. @d xxx4=242 {potentially long extension to \.{DVI} primitives}
  1040. @d fnt_def1=243 {define the meaning of a font number}
  1041. @d pre=247 {preamble}
  1042. @d post=248 {postamble beginning}
  1043. @d post_post=249 {postamble ending}
  1044. @d undefined_commands==250,251,252,253,254,255
  1045.  
  1046. @ The preamble contains basic information about the file as a whole. As
  1047. stated above, there are six parameters:
  1048. $$\hbox{|@!i[1]| |@!num[4]| |@!den[4]| |@!mag[4]| |@!k[1]| |@!x[k]|.}$$
  1049. The |i| byte identifies \.{DVI} format; currently this byte is always set
  1050. to~2. (Some day we will set |i=3|, when \.{DVI} format makes another
  1051. incompatible change---perhaps in 1992.)
  1052.  
  1053. The next two parameters, |num| and |den|, are positive integers that define
  1054. the units of measurement; they are the numerator and denominator of a
  1055. fraction by which all dimensions in the \.{DVI} file could be multiplied
  1056. in order to get lengths in units of $10^{-7}$ meters. (For example, there are
  1057. exactly 7227 \TeX\ points in 254 centimeters, and \TeX82 works with scaled
  1058. points where there are $2^{16}$ sp in a point, so \TeX82 sets |num=25400000|
  1059. and $|den|=7227\cdot2^{16}=473628672$.)
  1060. @^sp@>
  1061.  
  1062. The |mag| parameter is what \TeX82 calls \.{\\mag}, i.e., 1000 times the
  1063. desired magnification. The actual fraction by which dimensions are
  1064. multiplied is therefore $mn/1000d$. Note that if a \TeX\ source document
  1065. does not call for any `\.{true}' dimensions, and if you change it only by
  1066. specifying a different \.{\\mag} setting, the \.{DVI} file that \TeX\
  1067. creates will be completely unchanged except for the value of |mag| in the
  1068. preamble and postamble. (Fancy \.{DVI}-reading programs allow users to
  1069. override the |mag|~setting when a \.{DVI} file is being printed; this facility
  1070. has not been implemented in \.{DVItoLN03}.)
  1071.  
  1072. Finally, |k| and |x| allow the \.{DVI} writer to include a comment, which is not
  1073. interpreted further. The length of comment |x| is |k|, where |0<=k<256|.
  1074.  
  1075. @d id_byte=2 {identifies the kind of \.{DVI} files described here}
  1076.  
  1077. @ Font definitions for a given font number |k| contain further parameters
  1078. $$\hbox{|c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.}$$
  1079. The four-byte value |c| is the check sum that \TeX\ (or whatever program
  1080. generated the \.{DVI} file) found in the \.{TFM} file for this font;
  1081. |c| should match the check sum of the font found by programs that read
  1082. this \.{DVI} file.
  1083. @^check sum@>
  1084.  
  1085. Parameter |s| contains a fixed-point scale factor that is applied to the
  1086. character widths in font |k|; font dimensions in \.{TFM} files and other
  1087. font files are relative to this quantity, which is always positive and
  1088. less than $2^{27}$. It is given in the same units as the other dimensions
  1089. of the \.{DVI} file.  Parameter |d| is similar to |s|; it is the ``design
  1090. size,'' and it is given in \.{DVI} units that have not been corrected for
  1091. the magnification~|mag| found in the preamble.  Thus, font |k| is to be
  1092. used at $|mag|\cdot s/1000d$ times its normal size.
  1093.  
  1094. The remaining part of a font definition gives the external name of the font,
  1095. which is an ASCII string of length |a+l|. The number |a| is the length
  1096. of the ``area'' or directory, and |l| is the length of the font name itself;
  1097. the standard local system font area is supposed to be used when |a=0|.
  1098. The |n| field contains the area in its first |a| bytes.
  1099.  
  1100. Font definitions must appear before the first use of a particular font number.
  1101. Once font |k| is defined, it must not be defined again; however, we
  1102. shall see below that font definitions appear in the postamble as well as
  1103. in the pages, so in this sense each font number is defined exactly twice,
  1104. if at all. Like |nop| commands and \\{xxx} commands, font definitions can
  1105. appear before the first |bop|, or between an |eop| and a |bop|.
  1106.  
  1107. @ The last page in a \.{DVI} file is followed by `|post|'; this command
  1108. introduces the postamble, which summarizes important facts that \TeX\ has
  1109. accumulated about the file, making it possible to print subsets of the data
  1110. with reasonable efficiency. The postamble has the form
  1111. $$\vbox{\halign{\hbox{#\hfil}\cr
  1112.   |post| |p[4]| |num[4]| |den[4]| |mag[4]| |l[4]| |u[4]| |s[2]| |t[2]|\cr
  1113.   $\langle\,$font definitions$\,\rangle$\cr
  1114.   |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$
  1115. Here |p| is a pointer to the final |bop| in the file. The next three
  1116. parameters, |num|, |den|, and |mag|, are duplicates of the quantities that
  1117. appeared in the preamble.
  1118.  
  1119. Parameters |l| and |u| give respectively the height-plus-depth of the tallest
  1120. page and the width of the widest page, in the same units as other dimensions
  1121. of the file. These numbers might be used by a \.{DVI}-reading program to
  1122. position individual ``pages'' on large sheets of film or paper; however,
  1123. the standard convention for output on normal size paper is to position each
  1124. page so that the upper left-hand corner is exactly one inch from the left
  1125. and the top. Experience has shown that it is unwise to design \.{DVI}-to-printer
  1126. software that attempts cleverly to center the output; a fixed position of
  1127. the upper left corner is easiest for users to understand and to work with.
  1128. Therefore |l| and~|u| are often ignored.
  1129.  
  1130. Parameter |s| is the maximum stack depth (i.e., the largest excess of
  1131. |push| commands over |pop| commands) needed to process this file. Then
  1132. comes |t|, the total number of pages (|bop| commands) present.
  1133.  
  1134. The postamble continues with font definitions, which are any number of
  1135. \\{fnt\_def} commands as described above, possibly interspersed with |nop|
  1136. commands. Each font number that is used in the \.{DVI} file must be defined
  1137. exactly twice: Once before it is first selected by a \\{fnt} command, and once
  1138. in the postamble.
  1139.  
  1140. @ The last part of the postamble, following the |post_post| byte that
  1141. signifies the end of the font definitions, contains |q|, a pointer to the
  1142. |post| command that started the postamble.  An identification byte, |i|,
  1143. comes next; this currently equals~2, as in the preamble.
  1144.  
  1145. The |i| byte is followed by four or more bytes that are all equal to
  1146. the decimal number 223 (i.e., @'337 in octal). \TeX\ puts out four to seven of
  1147. these trailing bytes, until the total length of the file is a multiple of
  1148. four bytes, since this works out best on machines that pack four bytes per
  1149. word; but any number of 223's is allowed, as long as there are at least four
  1150. of them. In effect, 223 is a sort of signature that is added at the very end.
  1151. @^Fuchs, David Raymond@>
  1152.  
  1153. This curious way to finish off a \.{DVI} file makes it feasible for
  1154. \.{DVI}-reading programs to find the postamble first, on most computers,
  1155. even though \TeX\ wants to write the postamble last. Most operating
  1156. systems permit random access to individual words or bytes of a file, so
  1157. the \.{DVI} reader can start at the end and skip backwards over the 223's
  1158. until finding the identification byte. Then it can back up four bytes, read
  1159. |q|, and move to byte |q| of the file. This byte should, of course,
  1160. contain the value 248 (|post|); now the postamble can be read, so the
  1161. \.{DVI} reader discovers all the information needed for typesetting the
  1162. pages. Note that it is also possible to skip through the \.{DVI} file at
  1163. reasonably high speed to locate a particular page, if that proves
  1164. desirable. This saves a lot of time, since \.{DVI} files used in production
  1165. jobs tend to be large.
  1166.  
  1167. Unfortunately, however, standard \PASCAL\ does not include the ability to
  1168. @^system dependencies@>
  1169. access a random position in a file, or even to determine the length of a file.
  1170. Almost all systems nowadays provide the necessary capabilities, so \.{DVI}
  1171. format has been designed to work most efficiently with modern operating systems.
  1172. As noted above, \.{DVItoLN03} would need to be modified to limit itself to the
  1173. restrictions of standard \PASCAL\ if |random_reading| is defined to be |false|.
  1174.  
  1175. @* Font metric data.
  1176. The idea behind \.{TFM} files is that typesetting routines like \TeX\
  1177. need a compact way to store the relevant information about several
  1178. dozen fonts, and computer centers need a compact way to store the
  1179. relevant information about several hundred fonts. \.{TFM} files are
  1180. compact, and most of the information they contain is highly relevant,
  1181. so they provide a solution to the problem.
  1182.  
  1183. The information in a \.{TFM} file appears in a sequence of 8-bit bytes.
  1184. Since the number of bytes is always a multiple of 4, we could
  1185. also regard the file as a sequence of 32-bit words; but \TeX\ uses the
  1186. byte interpretation. Note that the bytes are considered to be unsigned
  1187. numbers.
  1188.  
  1189. @<Glob...@>=
  1190. @!tfm_file:byte_file; {a font metric file}
  1191.  
  1192. @ The first 24 bytes (6 words) of a \.{TFM} file contain twelve 16-bit
  1193. integers that give the lengths of the various subsequent portions
  1194. of the file. These twelve integers are, in order:
  1195. $$\vbox{\halign{\hfil#&$\null=\null$#\hfil\cr
  1196. |@!lf|&length of the entire file, in words;\cr
  1197. |@!lh|&length of the header data, in words;\cr
  1198. |@!bc|&smallest character code in the font;\cr
  1199. |@!ec|&largest character code in the font;\cr
  1200. |@!nw|&number of words in the width table;\cr
  1201. |@!nh|&number of words in the height table;\cr
  1202. |@!nd|&number of words in the depth table;\cr
  1203. |@!ni|&number of words in the italic correction table;\cr
  1204. |@!nl|&number of words in the lig/kern table;\cr
  1205. |@!nk|&number of words in the kern table;\cr
  1206. |@!ne|&number of words in the extensible character table;\cr
  1207. |@!np|&number of font parameter words.\cr}}$$
  1208. They are all nonnegative and less than $2^{15}$. We must have |bc-1<=ec<=255|,
  1209. |ne<=256|, and
  1210. $$\hbox{|lf=6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np|.}$$
  1211. Note that a font may contain as many as 256 characters (if |bc=0| and |ec=255|),
  1212. and as few as 0 characters (if |bc=ec+1|).
  1213.  
  1214. Incidentally, when two or more 8-bit bytes are combined to form an integer of
  1215. 16 or more bits, the most significant bytes appear first in the file.
  1216. This is called BigEndian order.
  1217.  
  1218. @ The rest of the \.{TFM} file may be regarded as a sequence of ten data
  1219. arrays having the informal specification
  1220. $$\def\arr$[#1]#2${\&{array} $[#1]$ \&{of} #2}
  1221. \vbox{\halign{\hfil\\{#}&$\,:\,$\arr#\hfil\cr
  1222. header&|[0..lh-1]stuff|\cr
  1223. char\_info&|[bc..ec]char_info_word|\cr
  1224. width&|[0..nw-1]fix_word|\cr
  1225. height&|[0..nh-1]fix_word|\cr
  1226. depth&|[0..nd-1]fix_word|\cr
  1227. italic&|[0..ni-1]fix_word|\cr
  1228. lig\_kern&|[0..nl-1]lig_kern_command|\cr
  1229. kern&|[0..nk-1]fix_word|\cr
  1230. exten&|[0..ne-1]extensible_recipe|\cr
  1231. param&|[1..np]fix_word|\cr}}$$
  1232. The most important data type used here is a |@!fix_word|, which is
  1233. a 32-bit representation of a binary fraction. A |fix_word| is a signed
  1234. quantity, with the two's complement of the entire word used to represent
  1235. negation. Of the 32 bits in a |fix_word|, exactly 12 are to the left of the
  1236. binary point; thus, the largest |fix_word| value is $2048-2^{-20}$, and
  1237. the smallest is $-2048$. We will see below, however, that all but one of
  1238. the |fix_word| values will lie between $-16$ and $+16$.
  1239.  
  1240. @ The first data array is a block of header information, which contains
  1241. general facts about the font. The header must contain at least two words,
  1242. and for \.{TFM} files to be used with Xerox printing software it must
  1243. contain at least 18 words, allocated as described below. When different
  1244. kinds of devices need to be interfaced, it may be necessary to add further
  1245. words to the header block.
  1246.  
  1247. \yskip\hang|header[0]| is a 32-bit check sum that \TeX\ will copy into the
  1248. \.{DVI} output file whenever it uses the font.  Later on when the \.{DVI}
  1249. file is printed, possibly on another computer, the actual font that gets
  1250. used is supposed to have a check sum that agrees with the one in the
  1251. \.{TFM} file used by \TeX. In this way, users will be warned about
  1252. potential incompatibilities. (However, if the check sum is zero in either
  1253. the font file or the \.{TFM} file, no check is made.)  The actual relation
  1254. between this check sum and the rest of the \.{TFM} file is not important;
  1255. the check sum is simply an identification number with the property that
  1256. incompatible fonts almost always have distinct check sums.
  1257. @^check sum@>
  1258.  
  1259. \yskip\hang|header[1]| is a |fix_word| containing the design size of the
  1260. font, in units of \TeX\ points (7227 \TeX\ points = 254 cm).  This number
  1261. must be at least 1.0; it is fairly arbitrary, but usually the design size
  1262. is 10.0 for a ``10 point'' font, i.e., a font that was designed to look
  1263. best at a 10-point size, whatever that really means. When a \TeX\ user
  1264. asks for a font `\.{at} $\delta$ \.{pt}', the effect is to override the
  1265. design size and replace it by $\delta$, and to multiply the $x$ and~$y$
  1266. coordinates of the points in the font image by a factor of $\delta$
  1267. divided by the design size.  {\sl All other dimensions in the\/\ \.{TFM}
  1268. file are |fix_word|\kern-1pt\ numbers in design-size units.} Thus, for example,
  1269. the value of |param[6]|, one \.{em} or \.{\\quad}, is often the |fix_word|
  1270. value $2^{20}=1.0$, since many fonts have a design size equal to one em.
  1271. The other dimensions must be less than 16 design-size units in absolute
  1272. value; thus, |header[1]| and |param[1]| are the only |fix_word| entries in
  1273. the whole \.{TFM} file whose first byte might be something besides 0 or
  1274. 255.  @^design size@>
  1275.  
  1276. \yskip\hang|header[2..11]|, if present, contains 40 bytes that identify
  1277. the character coding scheme. The first byte, which must be between 0 and
  1278. 39, is the number of subsequent ASCII bytes actually relevant in this
  1279. string, which is intended to specify what character-code-to-symbol
  1280. convention is present in the font.  Examples are \.{ASCII} for standard
  1281. ASCII, \.{TeX text} for fonts like \.{cmr10} and \.{cmti9}, \.{TeX math
  1282. extension} for \.{cmex10}, \.{XEROX text} for Xerox fonts, \.{GRAPHIC} for
  1283. special-purpose non-alphabetic fonts, \.{UNSPECIFIED} for the default case
  1284. when there is no information.  Parentheses should not appear in this name.
  1285. (Such a string is said to be in {\mc BCPL} format.)
  1286. @^coding scheme@>
  1287.  
  1288. \yskip\hang|header[12..16]|, if present, contains 20 bytes that name the
  1289. font family (e.g., \.{CMR} or \.{HELVETICA}), in {\mc BCPL} format.
  1290. This field is also known as the ``font identifier.''
  1291. @^family name@>
  1292. @^font identifier@>
  1293.  
  1294. \yskip\hang|header[17]|, if present, contains a first byte called the
  1295. |seven_bit_safe_flag|, then two bytes that are ignored, and a fourth byte
  1296. called the |face|. If the value of the fourth byte is less than 18, it has
  1297. the following interpretation as a ``weight, slope, and expansion'':  Add 0
  1298. or 2 or 4 (for medium or bold or light) to 0 or 1 (for roman or italic) to
  1299. 0 or 6 or 12 (for regular or condensed or extended).  For example, 13 is
  1300. 0+1+12, so it represents medium italic extended.  A three-letter code
  1301. (e.g., \.{MIE}) can be used for such |face| data.
  1302.  
  1303. \yskip\hang|header[18..@twhatever@>]| might also be present; the individual
  1304. words are simply called |header[18]|, |header[19]|, etc., at the moment.
  1305.  
  1306. @ Next comes the |char_info| array, which contains one |char_info_word|
  1307. per character. Each |char_info_word| contains six fields packed into
  1308. four bytes as follows.
  1309.  
  1310. \yskip\hang first byte: |width_index| (8 bits)\par
  1311. \hang second byte: |height_index| (4 bits) times 16, plus |depth_index|
  1312.   (4~bits)\par
  1313. \hang third byte: |italic_index| (6 bits) times 4, plus |tag|
  1314.   (2~bits)\par
  1315. \hang fourth byte: |remainder| (8 bits)\par
  1316. \yskip\noindent
  1317. The actual width of a character is |width[width_index]|, in design-size
  1318. units; this is a device for compressing information, since many characters
  1319. have the same width. Since it is quite common for many characters
  1320. to have the same height, depth, or italic correction, the \.{TFM} format
  1321. imposes a limit of 16 different heights, 16 different depths, and
  1322. 64 different italic corrections.
  1323.  
  1324. Incidentally, the relation |width[0]=height[0]=depth[0]=italic[0]=0|
  1325. should always hold, so that an index of zero implies a value of zero.
  1326. The |width_index| should never be zero unless the character does
  1327. not exist in the font, since a character is valid if and only if it lies
  1328. between |bc| and |ec| and has a nonzero |width_index|.
  1329.  
  1330. @ The remaining details of the format of \.{TFM} files need not concern us
  1331. further; the interested reader is referred to Knuth's standard \.{TFtoPL}
  1332. @^Knuth, D.~E.@>
  1333. @.TFtoPL@>
  1334. program for the whole picture.
  1335.  
  1336. @* Input from binary files.
  1337. We have seen that a \.{DVI} file is a sequence of 8-bit bytes. The bytes
  1338. appear physically in what is called a `|packed file of 0..255|'
  1339. in \PASCAL\ lingo.
  1340.  
  1341. Packing is system dependent, and many \PASCAL\ systems fail to implement
  1342. such files in a sensible way (at least, from the viewpoint of producing
  1343. good production software).  For example, some systems treat all
  1344. byte-oriented files as text, looking for end-of-line marks and such
  1345. things. Therefore some system-dependent code is often needed to deal with
  1346. binary files, even though most of the program in this section of
  1347. \.{DVItoLN03} is written in standard \PASCAL.
  1348. @^system dependencies@>
  1349.  
  1350. One common way to solve the problem is to consider files of |integer|
  1351. numbers, and to convert an integer in the range $-2^{31}\L x<2^{31}$ to
  1352. a sequence of four bytes $(a,b,c,d)$ using the following code, which
  1353. avoids the controversial integer division of negative numbers:
  1354. $$\vbox{\halign{#\hfil\cr
  1355. |if x>=0 then a:=x div @'100000000|\cr
  1356. |else begin x:=(x+@'10000000000)+@'10000000000; a:=x div @'100000000+128;|\cr
  1357. \quad|end|\cr
  1358. |x:=x mod @'100000000;|\cr
  1359. |b:=x div @'200000; x:=x mod @'200000;|\cr
  1360. |c:=x div @'400; d:=x mod @'400;|\cr}}$$
  1361. The four bytes are then kept in a buffer and output one by one. (On 36-bit
  1362. computers, an additional division by 16 is necessary at the beginning.
  1363. Another way to separate an integer into four bytes is to use/abuse
  1364. \PASCAL's variant records, storing an integer and retrieving bytes that are
  1365. packed in the same place; {\sl caveat implementor!\/}) It is also desirable
  1366. in some cases to read a hundred or so integers at a time, maintaining a
  1367. larger buffer.
  1368.  
  1369. Although plain \.{DVItype} sticks to simple \PASCAL, for reasons of clarity,
  1370. (even if such simplicity is sometimes unrealistic), we do things rather
  1371. differently under Vax/VMS, reading each file one disk block at a time.
  1372.  
  1373. @d VAX_block_length=512
  1374.  
  1375. @<Types...@>=
  1376. @!eight_bits=[byte] 0..255; {unsigned one-byte quantity}
  1377. @!sixteen_bits=[word] 0..65535; {unsigned two-byte quantity}
  1378. @!byte_block=packed array [0..VAX_block_length-1] of eight_bits;
  1379. @!byte_file=packed file of byte_block;
  1380.  
  1381.  
  1382. @ To prepare these files for input, we |reset| them. An extension of
  1383. \PASCAL\ is needed in the case of |tfm_file|, since we want to associate
  1384. it with external files whose names are specified dynamically (i.e., not
  1385. known at compile time). The following code uses the VAX-\PASCAL\ |open|
  1386. statement; where |open(f,s)| opens the file associated with file variable |f|
  1387. using the name provided by string variable |s|; once the file variable has
  1388. been associated with the correctly named file, the |reset| statement positions
  1389. the file for reading in the usual manner.
  1390. In the case of opening the |tfm_file|, we firstly close any earlier
  1391. one which may already have been opened. If |eof(f)| is true immediately after
  1392. the file has been opened, we assume that no file named |s| is accessible.
  1393. @^system dependencies@>
  1394.  
  1395. To improve the visual appearance of various VAX-\PASCAL\ file manipulation
  1396. statements, the following definition is used to represent the use of the
  1397. `named parameter' passing mechanism which instructs that any errors detected
  1398. shall be ignored, rather than causing a run-time error.
  1399.  
  1400. @d VAX_continue==@=error@>:=@=continue@>
  1401.  
  1402. @p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
  1403. begin reset(dvi_file);
  1404. dvi_count:=0; cur_block:=0;
  1405. cur_loc:=0;
  1406. end;
  1407. @#
  1408. procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
  1409.   var @!trimmed_name:file_spec; {Holds trimmed version of |cur_name|}
  1410.   @!i,@!j:0..name_length; {Indices into |cur_name|}
  1411. begin close(tfm_file,VAX_continue);
  1412. trimmed_name:=''; {Initialize to empty string}
  1413. i:=name_length;
  1414. while cur_name[i]=' ' do decr(i); {we know that the name isn't all spaces!}
  1415. for j:=1 to i do trimmed_name:=trimmed_name+cur_name[j];
  1416. open(tfm_file,cur_name,@=readonly@>,VAX_continue);
  1417. if status(tfm_file)<>0 then
  1418.   abort('Font not loaded, TFM file ',trimmed_name,' can''t be opened');
  1419. @:fatal error Font not loaded TFM file}{\qquad\.{TFM file can't be opened}@>
  1420. reset(tfm_file,VAX_continue);
  1421. tfm_count:=0;
  1422. end;
  1423.  
  1424. @ If you looked carefully at the preceding code, you probably asked,
  1425. ``What are |cur_loc| and |cur_name|?'' Good question. They're global
  1426. variables: |cur_loc| is the number of the byte about to be read next from
  1427. |dvi_file|, and |cur_name| is a string variable that will be set to the
  1428. current font metric file name before |open_tfm_file| is called.  When the
  1429. \.{DVI} file is first |open|ed, we'll make use of the |@=user_action@>| facility
  1430. of the VAX |open| procedure to note that actual file length in |dvi_size|.
  1431. This can then be used as the value of the |dvi_length| function.
  1432.  
  1433. Since we read a VAX/RMS blockfull of bytes from the \.{DVI} file, variable
  1434. |dvi_count| allows us to keep track of which byte of the current block is
  1435. the next to be ``read''.  Similarly, |tfm_count| tracks the current byte of
  1436. the block read from |tfm_file|.   |cur_block| tracks which particular block
  1437. of the whole file is currently in the buffer associated with |dvi_file|;
  1438. this is used when |random_reading| is used to skip around in the file.  No
  1439. analogous variable is required for the |tfm_file|.
  1440.  
  1441. @<Glob...@>=
  1442. @!cur_loc:integer; {where we are about to look, in |dvi_file|}
  1443. @!cur_name:packed array[1..name_length] of char; {external name,
  1444.   with no lower case letters}
  1445. @!dvi_size:integer; {Actual length of \.{DVI} file, in bytes}
  1446. @!dvi_count:integer; {number of bytes read from current block of |dvi_file|}
  1447. @!cur_block:integer; {relative position of current block of |dvi_file|}
  1448. @!tfm_count:integer; {number of bytes read from current block of |tfm_file|}
  1449.  
  1450. @ It turns out to be convenient to read four bytes at a time, when we are
  1451. inputting from \.{TFM} files. The input goes into global variables
  1452. |b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3|
  1453. the fourth.
  1454.  
  1455. @<Glob...@>=
  1456. @!b0,@!b1,@!b2,@!b3: eight_bits; {four bytes input at once}
  1457.  
  1458. @ The |read_tfm_word| procedure sets |b0| through |b3| to the next
  1459. four bytes in the current \.{TFM} file.
  1460. @^system dependencies@>
  1461.  
  1462. @d read_tfm_file(#)==begin
  1463.         if tfm_count=VAX_block_length then begin
  1464.                 get(tfm_file,VAX_continue); tfm_count:=0;
  1465.                 end;
  1466.         #:=tfm_file^[tfm_count];
  1467.         incr(tfm_count);
  1468.         end
  1469.  
  1470. @p procedure read_tfm_word;
  1471. begin read_tfm_file(b0); read_tfm_file(b1);
  1472. read_tfm_file(b2); read_tfm_file(b3);
  1473. end;
  1474.  
  1475. @ We shall use another set of simple functions to read the next byte or
  1476. bytes from |dvi_file|. There are seven possibilities, each of which is
  1477. treated as a separate function in order to minimize the overhead for
  1478. subroutine calls.
  1479. @^system dependencies@>
  1480.  
  1481. Things aren't quite as simple as that, because when processing |dvi|
  1482. commands to set characters on the page, it may prove necessary to take
  1483. further |dvi| commands from a sequence read from a virtual font (\\{q.v.}).
  1484. Therefore, these routines have the option of reading |dvi| bytes from
  1485. |vf[vf_take]|.  When reading |dvi| bytes from the \.{DVI} file, |vftake<0|.
  1486.  
  1487. (\\{N.B.} |vf_take| and |vf_ptr| will be dealt with \&{much} later.)
  1488.  
  1489. @d read_dvi_file(#)==if vf_take<0 then
  1490.         begin
  1491.         while dvi_count>=VAX_block_length do begin
  1492.                 get(dvi_file,VAX_continue);
  1493.                 dvi_count:=dvi_count-VAX_block_length; incr(cur_block);
  1494.                 end;
  1495.         #:=dvi_file^[dvi_count];
  1496.         incr(dvi_count); incr(cur_loc)
  1497.         end
  1498.         else
  1499.         begin {take ``input'' from stored sequence}
  1500.           if vf_take=vf_ptr then abort('no more virtual font bytes');
  1501. @:fatal error no more virtual font bytes}{\quad\.{no more virtual font bytes}@>
  1502.           #:=vf[vf_take]; incr(vf_take)
  1503.         end
  1504.  
  1505. @p function get_byte:integer; {returns the next byte, unsigned}
  1506. var b:eight_bits;
  1507. begin if eof(dvi_file) then get_byte:=0
  1508. else  begin read_dvi_file(b); get_byte:=b;
  1509.   end;
  1510. end;
  1511. @#
  1512. function signed_byte:integer; {returns the next byte, signed}
  1513. var b:eight_bits;
  1514. begin read_dvi_file(b);
  1515. if b<128 then signed_byte:=b @+ else signed_byte:=b-256;
  1516. end;
  1517. @#
  1518. function get_two_bytes:integer; {returns the next two bytes, unsigned}
  1519. var a,@!b:eight_bits;
  1520. begin read_dvi_file(a); read_dvi_file(b);
  1521. get_two_bytes:=a*256+b;
  1522. end;
  1523. @#
  1524. function signed_pair:integer; {returns the next two bytes, signed}
  1525. var a,@!b:eight_bits;
  1526. begin read_dvi_file(a); read_dvi_file(b);
  1527. if a<128 then signed_pair:=a*256+b
  1528. else signed_pair:=(a-256)*256+b;
  1529. end;
  1530. @#
  1531. function get_three_bytes:integer; {returns the next three bytes, unsigned}
  1532. var a,@!b,@!c:eight_bits;
  1533. begin read_dvi_file(a); read_dvi_file(b); read_dvi_file(c);
  1534. get_three_bytes:=(a*256+b)*256+c;
  1535. end;
  1536. @#
  1537. function signed_trio:integer; {returns the next three bytes, signed}
  1538. var a,@!b,@!c:eight_bits;
  1539. begin read_dvi_file(a); read_dvi_file(b); read_dvi_file(c);
  1540. if a<128 then signed_trio:=(a*256+b)*256+c
  1541. else signed_trio:=((a-256)*256+b)*256+c;
  1542. end;
  1543. @#
  1544. function signed_quad:integer; {returns the next four bytes, signed}
  1545. var a,@!b,@!c,@!d:eight_bits;
  1546. begin read_dvi_file(a); read_dvi_file(b); read_dvi_file(c); read_dvi_file(d);
  1547. if a<128 then signed_quad:=((a*256+b)*256+c)*256+d
  1548. else signed_quad:=(((a-256)*256+b)*256+c)*256+d;
  1549. end;
  1550.  
  1551. @ Finally we come to the routines that are used only if |random_reading| is
  1552. |true|. The driver program below needs two such routines: |dvi_length| should
  1553. compute the total number of bytes in |dvi_file|, possibly also
  1554. causing |eof(dvi_file)| to be true; and |move_to_byte(n)|
  1555. should position |dvi_file| so that the next |get_byte| will read byte |n|,
  1556. starting with |n=0| for the first byte in the file.
  1557. @^system dependencies@>
  1558.  
  1559. Such routines are, of course, highly system dependent. They are implemented
  1560. in this program by
  1561.  
  1562. \yskip\hang(1) utilizing the optional |user_action| parameter of the |open|
  1563. procedure, this allows us to determine the length of the file when it is
  1564. first opened; and
  1565.  
  1566. \yskip\hang(2), using the VAX-\PASCAL\ |find| procedure to read a specific
  1567. block of the file, and positioning to the correct byte by resetting the
  1568. |byte_block| pointer appropriately.
  1569.  
  1570. \noindent We also make extensive use of VAX-\PASCAL's |varying array|
  1571. extension, which
  1572. facilitates the handling of variable length character strings; without them, it
  1573. would be necessary to keep track of the length of each such string, in the
  1574. fashion used by \.{DVItype} to store font names.
  1575.  
  1576. The VAX-\PASCAL\ |find(f,block)| procedure reads (into file variable
  1577. |f|) the specified record of the file; these are numbered from 1 upwards,
  1578. and, since we are working with block-oriented files, each record consists of
  1579. an entire disk block.
  1580.  
  1581. @f varying==array
  1582. @d VAX_find_block==@= find@>
  1583.  
  1584. @p function dvi_length:integer;
  1585. begin dvi_length:=dvi_size; if dvi_size<0 then
  1586.         abort('Internal error (no dvi_length)');
  1587. @:fatal error internal error no dvi}{\qquad\.{(no dvi_length)}@>
  1588. end;
  1589. @#
  1590. procedure move_to_byte(n:integer);
  1591. begin if n div VAX_block_length <> cur_block then
  1592.         begin cur_block:=n div VAX_block_length;
  1593.                 VAX_find_block(dvi_file,cur_block+1,VAX_continue)
  1594.         end;
  1595.         dvi_count:=n mod VAX_block_length; cur_loc:=n;
  1596. end;
  1597.  
  1598. @* Reading the font information.
  1599. \.{DVI} file format does not include information about character widths, since
  1600. that would tend to make the files a lot longer. But a program that reads
  1601. a \.{DVI} file is supposed to know the widths of the characters that appear
  1602. in \\{set\_char} commands. Therefore \.{DVItoLN03} looks at the font metric
  1603. (\.{TFM}) files for the fonts that are involved.
  1604. @.TFM {\rm files}@>
  1605.  
  1606. The character-width data appears also in other files (e.g., in \.{GF} files
  1607. that specify bit patterns for digitized characters);
  1608. thus, it is usually possible for \.{DVI} reading programs to get by with
  1609. accessing only one file per font. \.{DVItype} has a comparatively easy
  1610. task in this regard, since it needs only a few words of information from
  1611. each font; the \.{DVItoLN03} program has to go to some pains to
  1612. deal with complications that arise when a large number of large font files
  1613. all need to be accessed simultaneously.
  1614.  
  1615. @ \.{DVItype} needed to know only two things about a
  1616. given character |c| in a given font |f|: (1)~Is |c| a legal character
  1617. in~|f|? (2)~If so, what is the width of |c|? It also needed to know the
  1618. symbolic name of each font, so it could be printed out, and to know
  1619. the approximate size of inter-word spaces in each font.
  1620.  
  1621. For \.{DVItoLN03}, some further information is required, such as which
  1622. character in a downloaded font is to be used to image character |c| in font
  1623. |f|, and whether this imaging can be performed by the LN03 itself (from
  1624. stored downloaded fonts) or whether the glyph needs to be imaged by
  1625. downloading a bitmapped representation each time it occurs, or even replacing
  1626. it by a solid rule if no suitable file of bitmaps is available.
  1627.  
  1628. The answers to these questions appear implicitly in the following data
  1629. structures. The current number of known fonts is |nf|. Each known font has
  1630. an internal number |f|, where |0<=f<nf|; the external number of this font,
  1631. i.e., its font identification number in the \.{DVI} file, is |font_num[f]|,
  1632. and the external name of this font is the string that occupies positions
  1633. |font_name[f]| through |font_name[f+1]-1| of the array |names|. The latter
  1634. array consists of |ASCII_code| characters, and |font_name[nf]| is its first
  1635. unoccupied position.
  1636.  
  1637. Various metrical data is saved for comparison with values to be read later
  1638. from the file(s) containing the characters' bitmaps: |font_check_sum|,
  1639. |font_scaled_size| and |font_design_size|.
  1640. A horizontal motion in the range |-4*font_space[f]<h<font_space[f]|
  1641. will be treated as a `kern' that merely advances the program's conception of
  1642. physical horizontal position; larger movements will result in computation of a
  1643. freshly rounded position, thus eliminating cumulative errors which might
  1644. otherwise lead to unsightly character placement on the page.
  1645.  
  1646. The legal characters run from |font_bc[f]| to |font_ec[f]|, inclusive; more
  1647. precisely, a given character |c| is valid in font |f| if and only if
  1648. |font_bc[f]<=c<=font_ec[f]| and |char_width(f)(c)<>invalid_width|.
  1649. To cut down on page faulting with small working sets, the dimensions of each
  1650. character are held within the |glyph_map| structure (see following section),
  1651. so that all information relating to a particular glyph is held in one
  1652. contiguous section of memory: the original \.{DVItype}, and earlier versions
  1653. of \.{DVItoLN03}, used to have separate arrays |width[0..max_glyphs]|, etc.,
  1654. which could require references to locations separated by |max_glyphs|
  1655. integers, making inefficient use of virtual memory.
  1656.  
  1657. To obviate reloading of fonts with identical names and scaling, a further
  1658. array |font_map[f]| ordinarily holds the value |f|, but can point to a
  1659. different set of metrics if such multiple loading has been avoided.
  1660.  
  1661. Virtual fonts will almost certainly introduce different physical fonts; such
  1662. fonts are given a relative reference number internally, but we will want to
  1663. supply a value for the |font_num| array that's unique to each such font.
  1664. Therefore we keep track of the highest font number allocated by \TeX\ and
  1665. then use this as a base value when reading virtual fonts.
  1666.  
  1667. @d invalid_width==@'17777777777
  1668.  
  1669. @<Glob...@>=
  1670. @!nf:0..max_fonts; {the number of known fonts}
  1671. @!font_num:array [0..max_fonts] of integer; {external font numbers}
  1672. @!font_name:array [0..max_fonts] of 0..name_size; {starting positions
  1673.   of external font names}
  1674. @!names:array [0..name_size] of ASCII_code; {characters of names}
  1675. @!font_check_sum:array [0..max_fonts] of integer; {check sums}
  1676. @!font_scaled_size:array [0..max_fonts] of integer; {scale factors}
  1677. @!font_design_size:array [0..max_fonts] of integer; {design sizes}
  1678. @!font_space:array [0..max_fonts] of integer; {boundary between ``small''
  1679.   and ``large'' spaces}
  1680. @!font_bc:array [0..max_fonts] of integer; {beginning characters in fonts}
  1681. @!font_ec:array [0..max_fonts] of integer; {ending characters in fonts}
  1682. @!font_map:array [0..max_fonts] of 0..max_fonts; {internal cross-reference}
  1683. @!highest_font:0..max_fonts;
  1684.  
  1685. @ An array |glyphs| has one element for each of the potential characters in
  1686. each font.  Each element is a record of type |glyph_info| (defined below).
  1687. When a font definition is first read, during the gathering of font usage
  1688. statistics in the first pass, the row of this array corresponding to the
  1689. font being defined is set to indicate that every character is |unused|. As
  1690. any character in this font is found in a wanted page during that first pass,
  1691. the usage is noted.  Later revisions take place when mapping characters from
  1692. \TeX's fonts to those dowloaded to the printer.
  1693.  
  1694. During the second pass through the wanted pages, the character codes in
  1695. |glyphs| are used to generate the \.{LN3} file.
  1696.  
  1697. |glyph_map(f)(c)=glyphs[glyph_base[f]+c]| permits us access to information
  1698. relating to an individual glyph; fields of this record, named |width|, |height|
  1699. and |depth| are accessible through macro
  1700. |char_width(f)(c)=glyphs[glyph_base[f]+c].width| and its analogues; this works
  1701. because |glyph_base[f]| contains the index into |glyphs| at which character~0
  1702. appears (or would appear, if |font_bc[f]>0|). |glyph_ptr| is the first unused
  1703. position of the |glyphs| array.
  1704.  
  1705. @d char_glyph_end(#)==#]
  1706. @d glyph_map(#)==glyphs[glyph_base[#]+char_glyph_end
  1707. @d char_width_end(#)==#].width
  1708. @d char_height_end(#)==#].height
  1709. @d char_depth_end(#)==#].depth
  1710. @d char_width(#)==glyphs[glyph_base[#]+char_width_end
  1711. @d char_height(#)==glyphs[glyph_base[#]+char_height_end
  1712. @d char_depth(#)==glyphs[glyph_base[#]+char_depth_end
  1713.  
  1714. @<Glob...@>=
  1715. @!glyphs: array [0..max_glyphs] of glyph_info;
  1716. @!glyph_base : array [0..max_fonts] of integer; {index into |glyphs|}
  1717. @!glyph_ptr : 0..max_glyphs; {index into |glyphs|, |width|, |height| and
  1718. |depth|}
  1719.  
  1720. @ The array |glyphs| contains elements whose fields are used as follows:
  1721.  
  1722. \yskip\centerline{\vbox{\offinterlineskip\hrule
  1723.   \halign{\vrule#&\strut\quad#\hfil\quad&\vrule#&\quad#\hfil\quad&\vrule#\cr
  1724.       height2pt&\omit&&\omit&\cr
  1725.       &\hfil Field Name\quad&&Used for&\cr
  1726.       height2pt&\omit&&\omit&\cr
  1727.       \noalign{\hrule}
  1728.       height2pt&\omit&&\omit&\cr
  1729.       &|width|&&Character width in \.{DVI} units&\cr
  1730.       &|height|&&Character height in \.{DVI} units&\cr
  1731.       &|depth|&&Character depth in \.{DVI} units&\cr
  1732.       &|font_code|&&Number of downloaded LN03 font containing glyph&\cr
  1733.       &|char_code|&&LN03 character code to access glyph&\cr
  1734.       &|loaded|&&Indicates status of glyph, as below&\cr
  1735.       height2pt&\omit&&\omit&\cr
  1736.     }\hrule
  1737.   }
  1738. }
  1739.  
  1740. The values that may be assumed by the |loaded| field are as follows:
  1741.  
  1742. \item{$\bullet$} when a font is first met, all characters are set with
  1743. the value |unused|;
  1744. \item{$\bullet$} as the document is scanned, whenever a particular glyph
  1745. is used this field is set to |wanted|;
  1746. \item{$\bullet$} other values are set once the wanted pages have been
  1747. scanned, and the character font downloads created.
  1748.  
  1749. \noindent Further values for the |loaded| field, and other fields, will be
  1750. defined for accessing virtual fonts.
  1751.  
  1752. @<Types...@>==
  1753. @!download_status = (@!unused,@!wanted@|@<Other values of |download_status|@>@+);@/
  1754. @!glyph_info =
  1755.   packed record
  1756.     @!width:integer; {character width, in \.{DVI} units}
  1757.     @!height:integer; {character height, in \.{DVI} units}
  1758.     @!depth:integer; {character depth, in \.{DVI} units}
  1759.     @!char_code : eight_bits;
  1760.     @!loaded : download_status;
  1761.     @!font_code : 0..max_fonts;
  1762.     @<Other fields of |glyph_info|@>@;
  1763.   end;
  1764.  
  1765.  
  1766. @ @<Set init...@>=
  1767. nf:=0; glyph_ptr:=0; font_name[0]:=0; font_space[0]:=0; font_map[0]:=0;
  1768. highest_font:=0;
  1769.  
  1770. @ It is, of course, a simple matter to print the name of a given font.
  1771.  
  1772. @p procedure print_font(@!f:integer); {|f| is an internal font number}
  1773. var k:0..name_size; {index into |names|}
  1774. begin if f=nf then print('UNDEFINED!')
  1775. @.UNDEFINED@>
  1776. else  begin for k:=font_name[f] to font_name[f+1]-1 do
  1777.     print(xchr[names[k]]);
  1778.   end;
  1779. end;
  1780.  
  1781. @ Auxiliary arrays |in_width|, |in_height| and |in_depth| are used to hold
  1782. the relevant sections of the |tfm_file| as they are
  1783. input. The global variable |tfm_check_sum| is set to the check sum that
  1784. appears in the current \.{TFM} file.
  1785.  
  1786. @<Glob...@>=
  1787. @!in_width:array[0..255] of integer; {\.{TFM} width data in \.{DVI} units}
  1788. @!in_height:array[0..15] of integer; {\.{TFM} height data in \.{DVI} units}
  1789. @!in_depth:array[0..15] of integer; {\.{TFM} depth data in \.{DVI} units}
  1790. @!tfm_check_sum:integer; {check sum found in |tfm_file|}
  1791.  
  1792. @ Here is a procedure that absorbs the necessary information from a
  1793. \.{TFM} file, assuming that the file has just been successfully reset
  1794. so that we are ready to read its first byte. (A complete description of
  1795. \.{TFM} file format appears in the documentation of \.{TFtoPL} and will
  1796. not be repeated here.) The procedure does not check the \.{TFM} file
  1797. for validity, nor does it give explicit information about what is
  1798. wrong with a \.{TFM} file that proves to be invalid; \.{DVI}-reading
  1799. programs need not do this, since \.{TFM} files are almost always valid,
  1800. and since the \.{TFtoPL} utility program has been specifically designed
  1801. to diagnose \.{TFM} errors. The procedure simply returns |false| if it
  1802. detects anything amiss in the \.{TFM} data.
  1803.  
  1804. There is a parameter, |z|, which represents the scaling factor being
  1805. used to compute the font dimensions; it must be in the range $0<z<2^{27}$.
  1806.  
  1807. @p function in_TFM(@!z:integer):boolean; {input \.{TFM} data or return |false|}
  1808. label 9997, {go here when the format is bad}
  1809.   9998,  {go here when the information cannot be loaded}
  1810.   9999;  {go here to exit}
  1811. var k:integer; {index for loops}
  1812. @!lh:integer; {length of the header data, in four-byte words}
  1813. @!nw:integer; {number of words in the width table}
  1814. @!nh:integer; {number of words in the height table}
  1815. @!nd:integer; {number of words in the depth table}
  1816. @!gp:0..max_glyphs; {new value of |glyph_ptr| after successful input}
  1817. @!alpha,@!beta:integer; {quantities used in the scaling computation}
  1818. begin @<Read past the header data; |goto 9997| if there is a problem@>;
  1819. @<Store character-width indices at the end of the |width| table@>;
  1820. @<Read and convert the width values, setting up the |in_width| table@>;
  1821. @<Move the widths from |in_width| to |width|, and append |pixel_width| values@>;
  1822. glyph_ptr:=gp; in_TFM:=true; goto 9999;
  1823. 9997: error('---not loaded, TFM file is bad');
  1824. @.TFM file is bad@>
  1825. 9998: in_TFM:=false;
  1826. 9999: end;
  1827.  
  1828. @ @<Read past the header...@>=
  1829. read_tfm_word; lh:=b2*256+b3;
  1830. read_tfm_word; font_bc[nf]:=b0*256+b1; font_ec[nf]:=b2*256+b3;
  1831. if font_ec[nf]<font_bc[nf] then font_bc[nf]:=font_ec[nf]+1;
  1832. @<Mark all glyphs of font |nf| as |unused|@>;
  1833. {now |gp=glyph_ptr+font_ec[nf]-font_bc[nf]+1|}
  1834. read_tfm_word; nw:=b0*256+b1;
  1835. if (nw=0)or(nw>256) then goto 9997;
  1836. nh:=b2*256+b3;
  1837. if (nh=0)or(nh>16) then goto 9997;
  1838. read_tfm_word; nd:=b0*256+b1;
  1839. if (nd=0)or(nd>16) then goto 9997;
  1840. for k:=2 to 3+lh do
  1841.   begin if eof(tfm_file) then goto 9997;
  1842.   read_tfm_word;
  1843.   if k=4 then
  1844.     if b0<128 then tfm_check_sum:=((b0*256+b1)*256+b2)*256+b3
  1845.     else tfm_check_sum:=(((b0-256)*256+b1)*256+b2)*256+b3;
  1846.   end;
  1847.  
  1848. @ Whenever the program reads a \.{TFM} file, either during the main
  1849. processing loop, or when such files are read in whilst mapping virtual fonts
  1850. to physical ones, the |glyph_map| row corresponding to the physical file is
  1851. preset to indicate that none of the glyphs are used, and also to signify
  1852. that, in the absence of further information, the characters are taken from
  1853. the current font.  This little segment of code used to appear in-line within
  1854. procedure |in_TFM|.
  1855.  
  1856. We also indicate here that no glyphs from the font as a whole are currently
  1857. used.  Once a glyph is referenced (either directly, or from within a
  1858. character packet in a virtual font), we'll mark the font as |wanted|, thus
  1859. indicating (so far as we currently know) the font \\{is} physical; this may
  1860. be changed later, if the font is found to be virtual. This information is
  1861. stored in yet another array, |font_type|.
  1862.  
  1863. @<Mark all glyphs of font |nf| as |unused|@>=
  1864. font_type[nf]:=unused;
  1865. if glyph_ptr+font_ec[nf]-font_bc[nf]+1 > max_glyphs then
  1866.   capacity_exceeded('too many glyphs: ',glyph_ptr+font_ec[nf]-font_bc[nf]+1:1,
  1867.         ' > ',max_glyphs:1);
  1868. @:capacity exceeded too many glyphs}{\quad\.{too many glyphs}@>
  1869. glyph_base[nf]:=glyph_ptr-font_bc[nf];
  1870. gp:=glyph_ptr+font_ec[nf]-font_bc[nf]+1;
  1871. for m:= glyph_ptr to gp-1 do
  1872.     with glyphs[m] do
  1873.     begin
  1874.         loaded := unused;
  1875.         font_code := nf
  1876.     end
  1877.  
  1878. @ At this point, we've arrived at the start of the |char_info| section of
  1879. the |tfm_file|; from each 32-bit word, we extract the indices into the
  1880. |width|, |height| and |depth| sections of the file.
  1881. @<Store character-width indices...@>=
  1882. if gp>0 then for k:=glyph_ptr to gp-1 do
  1883. with glyphs[k] do
  1884.   begin read_tfm_word;
  1885.   if b0>nw then goto 9997;
  1886.   width:=b0;
  1887.   if (b1 div 16)>nh then goto 9997;
  1888.   height:=b1 div 16;
  1889.   if (b1 mod 16)>nd then goto 9997;
  1890.   depth:=b1 mod 16;
  1891.   end;
  1892.  
  1893. @ The most important part of |in_TFM| is the width computation, which
  1894. involves multiplying the relative widths in the \.{TFM} file by the
  1895. scaling factor in the \.{DVI} file. This fixed-point multiplication
  1896. must be done with precisely the same accuracy by all \.{DVI}-reading programs,
  1897. in order to validate the assumptions made by \.{DVI}-writing programs
  1898. like \TeX82.
  1899.  
  1900. Let us therefore summarize what needs to be done. Each width in a \.{TFM}
  1901. file appears as a four-byte quantity called a |fix_word|.  A |fix_word|
  1902. whose respective bytes are $(a,b,c,d)$ represents the number
  1903. $$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr
  1904. b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=0;\cr
  1905. -16+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=255.\cr}}\right.$$
  1906. (No other choices of $a$ are allowed, since the magnitude of a \.{TFM}
  1907. dimension must be less than 16.)  We want to multiply this quantity by the
  1908. integer~|z|, which is known to be less than $2^{27}$. Let $\alpha=16z$.
  1909. If $|z|<2^{23}$, the individual multiplications $b\cdot z$, $c\cdot z$,
  1910. $d\cdot z$ cannot overflow; otherwise we will divide |z| by 2, 4, 8, or
  1911. 16, to obtain a multiplier less than $2^{23}$, and we can compensate for
  1912. this later. If |z| has thereby been replaced by $|z|^\prime=|z|/2^e$, let
  1913. $\beta=2^{4-e}$; we shall compute
  1914. $$\lfloor(b+c\cdot2^{-8}+d\cdot2^{-16})\,z^\prime/\beta\rfloor$$ if $a=0$,
  1915. or the same quantity minus $\alpha$ if $a=255$.  This calculation must be
  1916. done exactly, for the reasons stated above; the following program does the
  1917. job in a system-independent way, assuming that arithmetic is exact on
  1918. numbers less than $2^{31}$ in magnitude.
  1919.  
  1920. @<Read and convert the width values...@>=
  1921. @<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>;
  1922. for k:=0 to nw-1 do
  1923.   begin read_tfm_word;
  1924.   in_width[k]:=(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta;
  1925.   if b0>0 then if b0<255 then goto 9997
  1926.     else in_width[k]:=in_width[k]-alpha;
  1927.   end;
  1928. @<Read and convert the height and depth values@>
  1929.  
  1930. @ Similarly, we store away the sets of distinct character heights and
  1931. depths; we don't need to convert |z| again.
  1932.  
  1933. @<Read and convert the height and depth values@>=
  1934. for k:=0 to nh-1 do
  1935.   begin read_tfm_word;
  1936.   in_height[k]:=(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta;
  1937.   if b0>0 then if b0<255 then goto 9997
  1938.     else in_height[k]:=in_height[k]-alpha;
  1939.   end;
  1940. for k:=0 to nd-1 do
  1941.   begin read_tfm_word;
  1942.   in_depth[k]:=(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta;
  1943.   if b0>0 then if b0<255 then goto 9997
  1944.     else in_depth[k]:=in_depth[k]-alpha;
  1945.   end
  1946.  
  1947. @ @<Replace |z|...@>=
  1948. begin alpha:=16*z; beta:=16;
  1949. while z>=@'40000000 do
  1950.   begin z:=z div 2; beta:=beta div 2;
  1951.   end;
  1952. end
  1953.  
  1954. @ A \.{DVI}-reading program usually works with font files instead of
  1955. \.{TFM} files, so \.{DVItype} is atypical in that respect. \.{DVItoLN03}
  1956. actually checks information in the \.{TFM} file, \&{and} also reads an
  1957. appropriate \.{PXL} file.  Font files
  1958. should, however, contain exactly the same character width data that is
  1959. found in the corresponding \.{TFM}s; check sums are used to help
  1960. ensure this. In addition, font files usually also contain the widths of
  1961. characters in pixels, since the device-independent character widths of
  1962. \.{TFM} files are generally not perfect multiples of pixels.
  1963.  
  1964. The |pixel_width| field of the |glyph_map| contains this information; when
  1965. |glyphs[k].width| is the device-independent width of some character in
  1966. \.{DVI} units, |glyphs[k].pixel_width| is the corresponding width of that
  1967. character in an actual font. The macro |char_pixel_width| is set up to be
  1968. analogous to |char_width|.
  1969.  
  1970. Similarly, |pixel_height| and |pixel_depth| are available; these are used
  1971. for replacing the glyph for a character for which the required bitmaps are
  1972. unavailable by the appropriately-sized rule.
  1973.  
  1974. @d pixel_width_end(#)==#].pixel_width
  1975. @d pixel_height_end(#)==#].pixel_height
  1976. @d pixel_depth_end(#)==#].pixel_depth
  1977. @d char_pixel_width(#)==glyphs[glyph_base[#]+pixel_width_end
  1978. @d char_pixel_height(#)==glyphs[glyph_base[#]+pixel_height_end
  1979. @d char_pixel_depth(#)==glyphs[glyph_base[#]+pixel_depth_end
  1980.  
  1981. @<Other fields of |glyph_info|@>=
  1982. @!pixel_width, @!pixel_height, @!pixel_depth : integer;
  1983.  
  1984. @ The following variables are used to hold the conversion factors for
  1985. translating device-independent units into device pixels.
  1986.  
  1987. @<Glob...@>=
  1988. @!conv:real; {converts \.{DVI} units to pixels}
  1989. @!true_conv:real; {converts unmagnified \.{DVI} units to pixels}
  1990. @!numerator,@!denominator:integer; {stated conversion ratio}
  1991. @!mag:integer; {magnification factor times 1000}
  1992.  
  1993. @ The following code computes pixel widths by simply rounding the \.{TFM}
  1994. widths to the nearest integer number of pixels, based on the conversion factor
  1995. |conv| that converts \.{DVI} units to pixels. However, such a simple
  1996. formula will not be valid for all fonts, and it will often give results that
  1997. are off by $\pm1$ when a low-resolution font has been carefully
  1998. hand-fitted. For example, a font designer often wants to make the letter `m'
  1999. a pixel wider or narrower in order to make the font appear more consistent.
  2000. \.{DVI}-to-printer programs should therefore input the correct pixel width
  2001. information from font files whenever there is a chance that it may differ.
  2002. A warning message may also be desirable in the case that at least one character
  2003. is found whose pixel width differs from |conv*width| by more than a full pixel.
  2004. @^system dependencies@>
  2005. (And similarly for the height and depth values.)
  2006.  
  2007. @d pixel_round(#)==round(conv*(#))
  2008.  
  2009. @<Move the widths from |in_width| to |width|, and append |pixel_width| values@>=
  2010. if in_width[0]<>0 then goto 9997; {the first width should be zero}
  2011. if gp>0 then for k:=glyph_ptr to gp-1 do
  2012. with glyphs[k] do
  2013. begin
  2014.   if width=0 then
  2015.     begin width:=invalid_width; pixel_width:=0;
  2016.     end
  2017.   else  begin width:=in_width[width];
  2018.     pixel_width:=pixel_round(width);
  2019.     end;
  2020.   height:=in_height[height];
  2021.   pixel_height:=pixel_round(height);
  2022.   depth:=in_depth[depth];
  2023.   pixel_depth:=pixel_round(depth)
  2024. end
  2025.  
  2026. @* Output to the terminal. Whilst DVItoLN03 is processing \.{DVI} files, it
  2027. reports its progress by outputting various messages to the terminal: in
  2028. particular, when processing each page, it reports the page number, enclosed in
  2029. `\.{[]}' brackets.  Under Vax/VMS, output to the terminal is not normally
  2030. visible until |writeln| is called, since the operating system buffers output
  2031. until a line is complete.  However, this would mean, for example, that output
  2032. would only appear as each line was filled.
  2033.  
  2034. We therefore arrange to |open| the |output| file (synonymous with |term_out|)
  2035. specifying null carriage control; output may then be made to appear by calling
  2036. |write_ln|, but this will not give the usual accompanying newline.  To start
  2037. printing on a newline, the character sequence |crlf| must be output
  2038. explicitly.
  2039.  
  2040. The |term_out| file is used for terminal output.
  2041. @^system dependencies@>
  2042. The number of characters already output to a terminal line is counted
  2043. in |term_offset| so that a suitable line break may be chosen when the line
  2044. becomes ``full'.
  2045.  
  2046. Messages giving page number strings being processed are contained in a
  2047. variable of type |file_spec|, because some messages may contain VAX/VMS file
  2048. names.
  2049.  
  2050. @d term_out==output {the terminal, considered as an output file}
  2051.  
  2052. @<Glob...@>=
  2053.   @!term_offset : 0..line_length;
  2054.  
  2055. @ When each page is processed, the page number (as generated by \TeX) is written
  2056. out to the user's terminal, enclosed in `\.{[]}' brackets.  The procedure
  2057. |monitor| ensures that such output does not overlap the right-margin.  It
  2058. does this by counting the characters already output to the line; up to
  2059. |line_length| characters will be output.  We use here the VAX-\PASCAL-specific
  2060. function |length| which returns the number of characters in a |varying of char|
  2061. which are actually occupied.
  2062.  
  2063. @d VAX_length==@= length@>
  2064.  
  2065. @p procedure monitor(s : file_spec);
  2066.   begin if VAX_length(s)+2>line_length-term_offset then {Line is fullish!}
  2067.     begin write_ln(term_out,crlf); term_offset:=0; end;
  2068.     if (term_offset>0) and (s[1]='[') then      {Space before a |'['|}
  2069.     begin write(term_out,' '); incr(term_offset); end;
  2070.     write_ln(term_out,s);
  2071.     term_offset:=term_offset+VAX_length(s)
  2072.   end;
  2073.  
  2074. @*Displaying error messages.  Later we shall make extensive use of the
  2075. following definitions, particularly when scanning the \.{DVI} file.  However,
  2076. they are defined up here so that warnings may be issued by the code that
  2077. follows.  Normally (when invoked from within |do_page|) the variable |a| will
  2078. contain the byte number within the \.{DVI} file at which a problem was
  2079. encountered; however, to give |show| something to work with when these macros
  2080. are used elsewhere, we also define a constant of that name, which will thus
  2081. always be within scope.
  2082.  
  2083. @d show(#)==begin
  2084.     if a<0 then print_ln(#) else print_ln('[',a:1,'] ',#);
  2085.     if term_offset>0 then write_ln(crlf);
  2086.     write_ln(term_out,#,crlf); term_offset:=0
  2087.   end
  2088. @d error(#)==begin
  2089.   history := error_given;
  2090.   show('Error: ',#)
  2091. @.Error:@>
  2092. end
  2093. @d warning(#)==begin
  2094.   if history = spotless then history := warning_given;
  2095.   show('Warning: ',#)
  2096. @.Warning:@>
  2097. end
  2098.  
  2099. @<Constants...@>==
  2100.  a = -1;
  2101.  
  2102. @* Optional modes of output.
  2103. \.{DVItoLN03} will print different quantities of information based on some
  2104. options that the user must specify: the typeout can be confined to a
  2105. restricted subset of the pages by specifying the desired starting page and
  2106. the maximum number of pages.  Further options permit
  2107. the selection of a different position for the origin on the page, which
  2108. by default is set to the ``standard'' of one inch from the top and left
  2109. edges of the paper, and selection of ``portrait'' or ``landscape'' orientation
  2110. for the output on the printed page.  All options are selected through the
  2111. use of VAX/VMS command qualifiers, but procedure |dialog| could be adapted
  2112. to carry out an interactive dialogue with a user on systems without VMS'
  2113. excellent facilities for defining commands.
  2114.  
  2115. The starting page is specified by giving a sequence of 1 to 10 numbers or
  2116. asterisks separated by dots. For example, the specification `\.{1.*.-5}'
  2117. can be used to refer to a page output by \TeX\ when $\.{\\count0}=1$
  2118. and $\.{\\count2}=-5$. (Recall that |bop| commands in a \.{DVI} file
  2119. are followed by ten `count' values.) An asterisk matches any number,
  2120. so the `\.*' in `\.{1.*.-5}' means that \.{\\count1} is ignored when
  2121. specifying the first page. If several pages match the given specification,
  2122. \.{DVItoLN03} will begin with the earliest such page in the file. The
  2123. default specification `\.*' (which matches all pages) therefore denotes
  2124. the page at the beginning of the file.
  2125.  
  2126. This version of \.{DVItoLN03} uses the VMS Command Language Interpreter utility
  2127. routines to determine whether command line qualifiers are present, and to
  2128. fetch their values, which are then stored in the following variables.
  2129. @^Command Language Interpreter@>
  2130. @^system dependencies@>
  2131. The default options that hold unless over-ridden by command line qualifiers
  2132. are:
  2133.  
  2134. \yskip\hang$\bullet$ \.{/starting\_page}=\.{*}
  2135. \yskip\hang$\bullet$ \.{/number\_of\_pages}=\.{1000000}
  2136. \yskip\hang$\bullet$ \.{/top\_margin} = \.{300px}
  2137. \yskip\hang$\bullet$ \.{/left\_margin}=\.{300px}
  2138. \yskip\hang$\bullet$ \.{/orientation}=\.{portrait}
  2139.  
  2140. @<Glob...@>=
  2141. @!left_marg,@!top_marg : integer;
  2142. @!orientation,@!new_orient,@!cur_orient : page_orientation;
  2143. @!h_fuzz,@!v_fuzz:integer; {controls how pedantic program is}
  2144. @!page_len,@!page_wid,@!y_min,@!x_min : integer;
  2145. @!max_pages:integer; {at most this many |bop..eop| pages will be printed}
  2146. @!resolution:real; {pixels per inch}
  2147. @!new_mag:integer; {if positive, overrides the postamble's magnification}
  2148.  
  2149. @ This type allows us to keep track of whether characters are currently
  2150. being imaged in landscape or portrait orientation.
  2151.  
  2152. @<Types...@>=
  2153. @!page_orientation = (@!portrait,@!landscape);
  2154.  
  2155. @ The starting page specification is recorded in two global arrays called
  2156. |start_count| and |start_there|. For example, `\.{1.*.-5}' is represented
  2157. by |start_there[0]=true|, |start_count[0]=1|, |start_there[1]=false|,
  2158. |start_there[2]=true|, |start_count[2]=-5|.
  2159. We also set |start_vals=2|, to indicate that count 2 was the last one
  2160. mentioned. The other values of |start_count| and |start_there| are not
  2161. important, in this example.
  2162.  
  2163. @<Glob...@>=
  2164. @!start_count:array[0..9] of integer; {count values to select starting page}
  2165. @!start_there:array[0..9] of boolean; {is the |start_count| value relevant?}
  2166. @!start_vals:0..9; {the last count considered significant}
  2167. @!count:array[0..9] of integer; {the count values on the current page}
  2168.  
  2169. @ @<Set init...@>=
  2170. max_pages:=1000000; start_vals:=0; start_there[0]:=false;
  2171. top_marg:=300; left_marg:=300; orientation:=portrait;
  2172.  
  2173. @ Here is a simple subroutine that tests if the current page might be the
  2174. starting page.
  2175.  
  2176. @p function start_match:boolean; {does |count| match the starting spec?}
  2177. var k:0..9;  {loop index}
  2178. @!match:boolean; {does everything match so far?}
  2179. begin match:=true;
  2180. for k:=0 to start_vals do
  2181.   if start_there[k]and(start_count[k]<>count[k]) then match:=false;
  2182. start_match:=match;
  2183. end;
  2184.  
  2185. @ The global variable |buf_ptr| is used while scanning each line of input;
  2186. it points to the first unread character in |buffer|, into which the command
  2187. line interpreter places strings by interaction with the CLI interface
  2188. procedures.
  2189.  
  2190. @<Glob...@>=
  2191. @!buffer: varying[terminal_line_length] of char;
  2192. @!buf_ptr:0..terminal_line_length; {the number of characters read}
  2193.  
  2194. @ The compiler needs to know about the ``foreign'' routines used for access to
  2195. the Command Line Interpreter. VAX-\PASCAL\ permits the ``declaration'' of
  2196. external procedures, where the declaration consists of the procedure heading
  2197. alone, followed by the reserved word |extern|, in a manner analogous to
  2198. standard \PASCAL's |forward| directive.
  2199. @^system dependencies@>
  2200.  
  2201. We also require the following special definitions, types, variables and
  2202. procedures to be able to get information from the command line interpreter.
  2203. The macro |VAX_string_or_empty| is used so as not to confuse W{\mc EAVE}'s
  2204. formatting of these foreign procedure interfaces.
  2205.  
  2206.  
  2207. @d VAX_volatile==@=[volatile]@>
  2208. @d VAX_immed==@=%immed @>
  2209. @d VAX_external==@=[external]@>
  2210. @d VAX_descr==@=%descr @>
  2211. @d VAX_ref==@=%ref @>
  2212. @d VAX_cli_present==@= cli$present@>
  2213. @d VAX_cli_get_value==@= cli$get_value@>
  2214. @d VAX_cli_negated==@"000381F8
  2215. @d VAX_lib_find_file==@= lib$find_file@>
  2216. @#
  2217. @f VAX_volatile==do_nothing
  2218. @f VAX_descr==var
  2219. @f VAX_ref==var
  2220. @f VAX_immed==integer
  2221. @f VAX_external==var
  2222. @#
  2223. @d VAX_string_or_empty(#)==VAX_volatile varying[@!#] of char := VAX_immed 0
  2224.  
  2225. @f extern==forward
  2226.  
  2227. @<Procedures for init...@>=
  2228. VAX_external @+function VAX_cli_present(
  2229.   VAX_descr @!entity:VAX_string_or_empty(s1_len)) : integer; extern;@#@t\2@>
  2230.  
  2231. VAX_external @+function VAX_cli_get_value(
  2232.   VAX_descr @!entity:VAX_string_or_empty(s1_len);
  2233.   VAX_descr @!returns:VAX_string_or_empty(s2_len);
  2234.   VAX_ref @!retlen:VAX_volatile sixteen_bits := VAX_immed 0):integer;
  2235.   extern;@#@t\2@>
  2236.  
  2237. VAX_external @+function VAX_lib_find_file(
  2238.   VAX_descr @!part_spec:VAX_volatile file_spec;
  2239.   VAX_descr @!result_spec:VAX_volatile file_spec;
  2240.   VAX_ref @!context:VAX_volatile integer;
  2241.   VAX_descr @!def_spec:VAX_volatile file_spec := VAX_immed 0;
  2242.   VAX_descr @!related_spec:VAX_volatile file_spec := VAX_immed 0;
  2243.   VAX_ref @!stv_addr:VAX_volatile integer := VAX_immed 0;
  2244.   VAX_ref @!user_flags:VAX_volatile integer := VAX_immed 0): integer;
  2245.   extern;@#@t\2@>
  2246.  
  2247. @ The following function |get_value| inputs the string corresponding to a
  2248. command-line qualifier and converts it to its numerical value. Calls of the
  2249. |VAX_cli_get_value(s,a)| return to the buffer parameter |a| the value placed
  2250. by the user on the command qualifier whose name is given by the string |s|;
  2251. being a system service type function, it also reports its success (or failure)
  2252. as its result.  The other system service used is |VAX_cli_present(s)| which
  2253. returns an odd (success) status indication if the qualifier whose name is
  2254. given by the string |s| \&{is} present in the command line.
  2255.  
  2256. One VAX-\PASCAL\ extension permits string variables to be used in place of
  2257. files for reading and writing values.  The |VAX_string_read(b,i,...)|
  2258. procedure reads values into variables |i,...| from the characters stored in
  2259. the string variable |b|.  The |VAX_string_status| function returns a value in
  2260. a manner entirely analogous to VAX-\PASCAL's external I/O |status| function,
  2261. but relating to the success of the previous string read.
  2262.  
  2263. @d VAX_string_read==@= readv@>
  2264. @d VAX_string_status==@= statusv@>
  2265.  
  2266. @p function get_value(qualifier: varying [$u1] of char):integer;
  2267.   var @!i,@!stat,@!len : integer; {local workspace}
  2268.     buffer: varying [terminal_line_length] of char; {buffer for qualifier
  2269.     string}
  2270. begin
  2271.   if odd(VAX_cli_present(qualifier)) then
  2272.   begin stat:=VAX_cli_get_value(qualifier,buffer); buffer:=buffer+' ';
  2273.   {append a space to ensure end can be found}
  2274.     VAX_string_read(buffer,i,VAX_continue);
  2275.     if VAX_string_status > 0 then
  2276.       abort('Bad /',qualifier,' value! (=',buffer,')')
  2277. @:fatal error Bad qualifier}{\quad \.{Bad /}\meta{qualifier} \.{value}@>
  2278.     else
  2279.       get_value:=i
  2280.   end else get_value:=-1
  2281. end;
  2282.  
  2283. @ Here is a routine that scans a (possibly signed) integer and computes
  2284. the decimal value. If no decimal integer starts at |buf_ptr|, the
  2285. value 0 is returned. The integer should be less than $2^{31}$ in
  2286. absolute value.
  2287.  
  2288. @p function get_integer:integer;
  2289. var x:integer; {accumulates the value}
  2290. @!negative:boolean; {should the value be negated?}
  2291. begin if buffer[buf_ptr]='-' then
  2292.   begin negative:=true; incr(buf_ptr);
  2293.   end
  2294. else negative:=false;
  2295. x:=0;
  2296. while (buffer[buf_ptr]>='0')and(buffer[buf_ptr]<='9') do
  2297.   begin x:=10*x+xord[buffer[buf_ptr]]-"0"; incr(buf_ptr);
  2298.   end;
  2299. if negative then get_integer:=-x @+ else get_integer:=x;
  2300. end;
  2301.  
  2302. @ One feature of the new DEClaser~2200 (otherwise known as the LN06) is a
  2303. capability of selecting from which paper tray a sheet shall be fed; the
  2304. printer has two separate trays, which can be loaded with different sizes or
  2305. colours of paper: a popular use is to have letterhead paper loaded in one
  2306. tray, and plain paper for continuation sheets in the other.  Both on this
  2307. printer, and on the DEClaser~2100 (LN05), any of five different types of
  2308. paper tray can be inserted into (respectively, either, or the only) tray
  2309. slot.  Paper cassettes are available to hold three different American sizes
  2310. of paper (letter, legal or executive), the ISO standard A4 size used in
  2311. Europe, or a stack of up to 15 envelopes.
  2312.  
  2313. The DEClaser~2200 can be fitted with a separate envelope feeder (in addition
  2314. to the two paper trays), which can hold up to 40~envelopes.  This envelope
  2315. feeder is supported by the program, but we don't (yet!) support one
  2316. further option of the LN06 which is a large capacity input tray that can
  2317. hold 1000 sheets.
  2318.  
  2319. In addition, both the 2100 and 2200 models can await manual feeding of
  2320. sheets.  Selection of paper source is not supported on the LN03, of course.
  2321.  
  2322. \noindent Here are the values that may be listed with the \.{/feed\_tray}
  2323. qualifier:
  2324. \yskip\hang\.{ALL=}\meta{tray type} All sheets will be fed from the
  2325. specified tray.
  2326. \yskip\hang\.{FIRST=}\meta{tray type} Feed the first sheet from the
  2327. specified tray; subsequent sheets will be fed from the \.{DEFAULT\_TRAY},
  2328. unless the next qualifier is used:
  2329. \yskip\hang\.{REST=}\meta{tray type} Feed the second and subsequent sheets
  2330. from the specified tray; the first sheet will be fed from the
  2331. \.{DEFAULT\_TRAY}, unless the \.{FIRST} tray has also been specified.
  2332.  
  2333. \noindent\meta{Tray types} can take the following values:
  2334. \yskip\hang\.{DEFAULT\_TRAY} Use whichever tray is set as the default by the
  2335. printer's initialization sequence.
  2336. \yskip\hang\.{TOP\_TRAY} (This is the only tray on the DEClaser~2100.)
  2337. \yskip\hang\.{BOTTOM\_TRAY}
  2338. \yskip\hang\.{ENVELOPE\_TRAY} Feed envelopes, from the large-capacity
  2339. envelope tray option on the DEClaser~2200.
  2340. \yskip\hang\.{MANUAL\_FEED} Wait for the operator to insert each sheet
  2341. manually.
  2342.  
  2343. \noindent The following values look as though they could be defined as a
  2344.           \PASCAL\ enumeration type, but we need to ensure a particular
  2345.           mapping to ordinal values, so we engage in a Knuthian trick here.
  2346. @^Knuth, D.~E.@>
  2347. @:TeX the Program}{{\sl\TeX\ the Program}@>
  2348. The values correspond to the $P_s$ parameter of the |DECASTC| (Automatic
  2349. Sheet-feeder Tray Control) control sequence.
  2350.  
  2351. @d default_tray=0
  2352. @d top_tray=1
  2353. @d bottom_tray=2
  2354. @d envelope_feeder=3
  2355. @d manual_feed=99
  2356.  
  2357. @<Types...@>=
  2358. @!tray_type=default_tray..manual_feed;
  2359.  
  2360. @ Here is a procedure which converts the \.{/feed\_tray} qualifier to the
  2361. appropriate numeric value.
  2362.  
  2363. @p procedure @!read_tray(option:file_spec; var result:tray_type);
  2364. begin
  2365.   result:=default_tray;
  2366.   if odd(VAX_cli_present(option+'.TOP_TRAY')) then
  2367.     result:=top_tray
  2368.   else if odd(VAX_cli_present(option+'.BOTTOM_TRAY')) then
  2369.     result:=bottom_tray
  2370.   else if odd(VAX_cli_present(option+'.ENVELOPE_TRAY')) then
  2371.     result:=envelope_feeder
  2372.   else if odd(VAX_cli_present(option+'.MANUAL_FEED')) then
  2373.     result:=manual_feed;
  2374.   if device_type=ln03 then result:=default_tray
  2375. end;
  2376. @ The selected options are put into global variables by the |dialog|
  2377. procedure, which is called just as \.{DVItoLN03} begins.  As already
  2378. explained, the |term_out| file is opened during the initialization phase, with
  2379. \.{carriage\_control}=\.{none}.  Having announced the program's name, the
  2380. command line interpreter library procedures are invoked to retrieve the
  2381. qualifiers and file name from the command line.
  2382.  
  2383. @p procedure dialog;
  2384. var k:integer; {loop variable}
  2385. begin rewrite(term_out); {prepare the terminal for output}
  2386. write_ln(term_out,banner,crlf);@/
  2387. @<Determine the desired |start_count| values@>;
  2388. @<Determine the desired |max_pages|@>;
  2389. @<Determine the desired |resolution|@>;
  2390. @<Determine the desired |new_mag|@>;
  2391. @<Determine the desired |left_marg| and |top_marg|@>;
  2392. @<Determine the desired |orientation|@>;
  2393. @<Determine the desired ``fuzzes''@>;
  2394. @<Determine the desired |print_mode|@>;
  2395. @<Determine the desired \\{feed\_trays}@>;
  2396. @<Determine the desired |duplex_by_page_numbers|@>;
  2397. @<Print all the selected options@>;
  2398. end;
  2399.  
  2400.  
  2401. @ @<Determine the desired |start...@>=
  2402. start_vals:=0; start_there[0]:=false;
  2403. if odd(VAX_cli_present('STARTING_PAGE')) then
  2404.   begin k:=VAX_cli_get_value('STARTING_PAGE',buffer); buffer:=buffer+' ';
  2405.   if (buffer[1]<>' ') then begin
  2406.     buf_ptr:= 1; k:=0;
  2407.     if buffer[1]<>' ' then
  2408.       repeat if buffer[buf_ptr]='*' then
  2409.         begin start_there[k]:=false; incr(buf_ptr);
  2410.         end
  2411.       else begin start_there[k]:=true; start_count[k]:=get_integer;
  2412.         end;
  2413.       if (k<9)and(buffer[buf_ptr]='.') then
  2414.         begin incr(k); incr(buf_ptr);
  2415.         end
  2416.       else start_vals:=k { anything ill-formed we just stop }
  2417.       until start_vals=k
  2418.    end
  2419. end
  2420.  
  2421. @ @<Determine the desired |max_pages|@>=
  2422. max_pages:=1000000;
  2423. max_pages:=get_value('NUMBER_OF_PAGES');
  2424. if max_pages<=0 then max_pages:=1000000;
  2425.  
  2426. @ @<Determine the desired |resolution|@>=
  2427. resolution:=300.0; { this is the LN03's resolution }
  2428.  
  2429. @ @<Determine the desired |new_mag|@>=
  2430. new_mag:=0; { no provision for magnified LN03 output yet }
  2431.  
  2432. @ The value associated with the \.{/left\UL margin} and \.{/top\UL margin}
  2433. qualifiers consists of a number and a specifier for a \TeX\ \meta{physical
  2434. unit}.  The values provided are translated into pixels; this uses the same
  2435. mechanism as is used for interpreting similar dimensions in \.{\BS special}
  2436. commands, so the qualifier is copied into the |spec_par| buffer used for
  2437. that interpretation.  The characters have to be converted from external
  2438. representation into |ASCII_code| for this.
  2439.  
  2440. @<Determine the desired |left_marg|...@>=
  2441. if odd(VAX_cli_present('LEFT_MARGIN')) then
  2442. begin
  2443.   k:=VAX_cli_get_value('LEFT_MARGIN',buffer); buffer:=buffer+' ';
  2444.   @<Copy qualifier into |spec_par|@>;
  2445.   if rd_dimension(left_marg) then
  2446.   begin
  2447.     print_ln(' --- assuming pixel units');
  2448.     write_ln(' --- assuming pixel units',crlf);
  2449.     term_offset:=0
  2450.   end
  2451. @.assuming pixel units@>
  2452. end else left_marg := -1;
  2453. if left_marg<0 then left_marg:=300;
  2454.  
  2455. if odd(VAX_cli_present('TOP_MARGIN')) then
  2456. begin
  2457.   k:=VAX_cli_get_value('TOP_MARGIN',buffer); buffer:=buffer+' ';
  2458.   @<Copy qualifier into |spec_par|@>;
  2459.   if rd_dimension(top_marg) then
  2460.   begin
  2461.     print_ln(' --- assuming pixel units');
  2462.     write_ln(' --- assuming pixel units',crlf);
  2463.     term_offset:=0
  2464.   end
  2465. @.assuming pixel units@>
  2466. end else top_marg := -1;
  2467. if top_marg<0 then top_marg:=300;
  2468.  
  2469. @ Here's how we copy the |char| values from the |buffer| (whence they have
  2470. been read from the command-line qualifier) into the |spec_par| buffer,
  2471. converting to |ASCII_code| in the process.
  2472.  
  2473. @<Copy qualifier into |spec_par|@>=
  2474.   buf_ptr:=1; spec_len:=1;
  2475.   while buf_ptr<=buffer.length do
  2476.   begin
  2477.     spec_par[spec_len]:=xord[buffer[buf_ptr]];
  2478.     incr(spec_len); incr(buf_ptr)
  2479.   end;
  2480.   spec_ptr:=1
  2481.  
  2482. @ When the paper orientation is selected, we may define the |page_len| and
  2483. |page_wid| in terms of the physical limits of the paper; these sizes have
  2484. been defined at the start of this \.{web}.
  2485.  
  2486. @<Determine the desired |orientation|@>=
  2487. if odd(VAX_cli_present('ORIENTATION')) then
  2488. begin
  2489.   if odd(VAX_cli_present('PORTRAIT')) then orientation:=portrait else
  2490.   if odd(VAX_cli_present('LANDSCAPE')) then orientation:=landscape else
  2491.   warning('/ORIENTATION must be "PORTRAIT" or "LANDSCAPE"; assuming "PORTRAIT"')
  2492. @:Warning: orientation must be}{\quad \.{/ORIENTATION must be ...}@>
  2493. end;
  2494. if orientation=landscape then
  2495. begin page_len:=paper_wid+page_x_min; page_wid:=paper_ht+y_land_min;
  2496.   y_min:=page_x_min; x_min:=y_land_min end else
  2497. begin page_len:=paper_ht+y_port_min; page_wid:=paper_wid+page_x_min;
  2498.   y_min:=y_port_min; x_min:=page_x_min end;
  2499.  
  2500. @ The value associated with the \.{/hfuzz} and \.{/vfuzz}
  2501. qualifiers consists of a number and a specifier for a \TeX\ \meta{physical
  2502. unit}.  The values provided are translated into scaled points, entirely
  2503. analogously with the translation of the value on the \.{/top\_margin}
  2504. qualifier into pixels.
  2505.  
  2506. These ``fuzz'' values are used to control how pedantic the program is when
  2507. it discovers characters which extend beyond the right-hand margin as
  2508. reported (in the \.{DVI} file) by \TeX.  The default values for these
  2509. qualifiers is \.{100sp}, which implies that ``errors'' smaller than this
  2510. (it's a very small quantity, approximately equal to the wavelength of
  2511. visible light) will be ignored.  Normally \TeX\ wouldn't dream of exceeding
  2512. the margin (well, not without issuing an \.{overfull \\hbox} warning), but
  2513. with some macro packages it isn't so fussy. For example, the output of W{\sc
  2514. EAVE}, when formatting the \PASCAL\ part of a program, can extend beyond the
  2515. margin by up to \.{10pt}.  By selecting a larger value of ``fuzz'',
  2516. spurious warning messages from DVItoLN03 can be suppressed.
  2517.  
  2518. @<Determine the desired ``fuzzes''@>=
  2519. if odd(VAX_cli_present('HFUZZ')) then
  2520. begin
  2521.   k:=VAX_cli_get_value('HFUZZ',buffer); buffer:=buffer+' ';
  2522.   @<Copy qualifier into |spec_par|@>;
  2523.   if rd_scaled_pt(h_fuzz) then
  2524.   begin
  2525.     print_ln(' --- assuming scaled points');
  2526. @.assuming scaled points@>
  2527.     write_ln(' --- assuming scaled points',crlf);
  2528.     term_offset:=0
  2529.   end
  2530. end else h_fuzz := 0;
  2531. decr(h_fuzz); {when used, we watch for |h-max_h| exceeding this value}
  2532. if h_fuzz<0 then h_fuzz:=99; {corresponds to default of \.{100sp}}
  2533.  
  2534. if odd(VAX_cli_present('VFUZZ')) then
  2535. begin
  2536.   k:=VAX_cli_get_value('VFUZZ',buffer); buffer:=buffer+' ';
  2537.   @<Copy qualifier into |spec_par|@>;
  2538.   if rd_scaled_pt(v_fuzz) then
  2539.   begin
  2540.     print_ln(' --- assuming scaled points');
  2541. @.assuming scaled points@>
  2542.     write_ln(' --- assuming scaled points',crlf);
  2543.     term_offset:=0
  2544.   end
  2545. end else v_fuzz := 0;
  2546. decr(v_fuzz);
  2547. if v_fuzz<0 then v_fuzz:=99
  2548.  
  2549. @ The program can support the traditional LN03 (and LN03-plus) printers (but
  2550. not the LN03R ScriptPrinter, for which a DVI to PostScript program
  2551. @^PostScript@>
  2552. is required), the LN05 (DEClaser~2100) and the LN06 (DEClaser~2200).
  2553.  
  2554. This variable reminds of which mode is in use:
  2555.  
  2556. @<Glob...@>=
  2557. @!device_type:(@!ln03,@!ln05,@!ln06);
  2558.  
  2559. @ This section determines which value was used with the \.{/DEVICE\_TYPE}
  2560. qualifier.  It is an error (something's gone wrong with the command
  2561. definition file) if this qualifier doesn't have a value.
  2562.  
  2563. @<Determine the device type@>=
  2564. device_type:=ln03;
  2565. if not odd(VAX_cli_present('DEVICE_TYPE')) then
  2566.   abort('No device type specified')
  2567. @:fatal error No device type specified}{\quad\.{No device type specified}@>
  2568. else
  2569.   if odd(VAX_cli_present('DEVICE_TYPE.LN03')) then
  2570.     device_type:=ln03
  2571.   else
  2572.   if odd(VAX_cli_present('DEVICE_TYPE.LN05')) then
  2573.     device_type:=ln05
  2574.   else
  2575.   if odd(VAX_cli_present('DEVICE_TYPE.LN06')) then
  2576.     device_type:=ln06
  2577.  
  2578. @ The new DEClaser printers (models 2100 and 2200, otherwise known as the
  2579. LN05 and LN06) are capable of a number of extra ``tricks'', compared with
  2580. the original LN03; in particular, the DEClaser~2200 can print on both sides
  2581. of the paper (``duplex'' printing).  The \.{/print\_mode} qualifier of the
  2582. command line interface allows us to select a number of different modes of
  2583. operation.
  2584.  
  2585. \noindent The possible modes are as follows:
  2586. \yskip\hang\.{SIMPLEX} Print on one side of paper only; this is the default,
  2587. and the only method compatible with the DEClaser~2100 (LN05) and the
  2588. original LN03 (but see below).
  2589. \yskip\hang\.{DUPLEX} Print on both sides of the paper; which page gets
  2590. printed on the reverse of such a sheet is further controlled by the
  2591. \.{/duplex\_by\_page\_numbers} qualifier.
  2592. \yskip\hang\.{MASTER} Can be used in conjunction with the \.{DUPLEX} value
  2593. to produce a master set of sheets for photocopying or other production
  2594. process.  The program behaves as if the printer were operating in duplex
  2595. mode, but actually prints on only one side of the paper, and ``prints'' a
  2596. blank page if it would have left the \\{verso} page blank.  This option can
  2597. be used with the DEClaser~2100 (LN05), and with the traditional LN03, even
  2598. though those printers don't support genuine duplexing.
  2599. \yskip\hang\.{TUMBLED} Can be used in conjunction with genuine duplex
  2600. printing only (and thus is restricted to the DEClaser~2200).  Causes
  2601. \\{verso} (left-hand) pages to be printed upside-down, therefore making the
  2602. final document suitable for binding along the short edge (long edge in
  2603. landscape mode) to make a flip-chart style document.
  2604. \yskip\hang\.{NORMAL} The opposite of \.{TUMBLED}.  This is the default.
  2605.  
  2606. \noindent Once again, we define an `enumeration' type with known values:
  2607.  
  2608. @d default_print_mode=0
  2609. @d true_simplex_normal=1
  2610. @d true_simplex_tumbled=2
  2611. @d true_duplex_normal=3
  2612. @d true_duplex_tumbled=4
  2613. @d duplex_master_normal=5
  2614. @d duplex_master_tumbled=6
  2615. @d ln03_master=7  {we have to handle this by software, since no printer support}
  2616.  
  2617. @<Glob...@>=
  2618. @!print_mode:default_print_mode..ln03_master;
  2619.  
  2620. @ Here's where we analyse the \.{/print\_mode} qualifier, and set
  2621. |print_mode| accordingly:
  2622.  
  2623. @<Determine the desired |print_mode|@>=
  2624. @<Determine the device type@>;
  2625. print_mode:=default_print_mode;
  2626. if odd(VAX_cli_present('PRINT_MODE')) then
  2627.   if odd(VAX_cli_present('PRINT_MODE.DUPLEX')) then
  2628.     if odd(VAX_cli_present('PRINT_MODE.MASTER')) then
  2629.       if odd(VAX_cli_present('PRINT_MODE.TUMBLED')) then
  2630.         print_mode:=duplex_master_tumbled
  2631.       else print_mode:=duplex_master_normal
  2632.     else
  2633.       if odd(VAX_cli_present('PRINT_MODE.TUMBLED')) then
  2634.         print_mode:=true_duplex_tumbled
  2635.       else print_mode:=true_duplex_normal
  2636.   else
  2637.     if odd(VAX_cli_present('PRINT_MODE.TUMBLED')) then
  2638.       print_mode:=true_simplex_tumbled
  2639.     else print_mode:=true_simplex_normal;
  2640. if device_type=ln03 then
  2641.   if print_mode>=duplex_master_normal then
  2642.     print_mode:=ln03_master
  2643.   else
  2644.     print_mode:=default_print_mode
  2645.  
  2646. @ Now we decide which tray shall be used for the first and subsequent sheets
  2647.  
  2648. @<Determine the desired \\{feed\_trays}@>=
  2649. if odd(VAX_cli_present('FEED_TRAY')) then
  2650.   if odd(VAX_cli_present('FEED_TRAY.ALL')) then
  2651.   begin
  2652.     read_tray('FEED_TRAY.ALL',first_tray);
  2653.     following_tray:=first_tray
  2654.   end
  2655.   else
  2656.   begin
  2657.     read_tray('FEED_TRAY.FIRST',first_tray);
  2658.     read_tray('FEED_TRAY.REST',following_tray)
  2659.   end
  2660.  
  2661. @ Here are the variables needed to save the print tray(s) selection; also
  2662. one which determines in which manner duplexing shall take place
  2663.  
  2664. @<Glob...@>=
  2665. @!first_tray,@!following_tray:tray_type;
  2666. @!duplex_by_page_numbers:boolean;
  2667.  
  2668. @ When we are printing on both sides of the paper, or simulating that
  2669. operation with the \.{MASTER} option on the \.{/PRINT\_MODE} qualifier, we
  2670. will usually elect to print \\{verso} pages when the page number (held in
  2671. |count[0]|) is even, and \\{recto} pages when it is odd (or zero).  If two
  2672. odd or two even pages appear in succession, the other side of the sheet will
  2673. be left blank (or a blank page ejected in \.{MASTER} mode).
  2674.  
  2675. This is the default mode of operation, when the \.{/DUPLEX\_BY\_PAGE\_NUMBERS}
  2676. qualifier is present; if this qualifier is negated, then the \\{verso} side
  2677. of the sheet will be covered with whatever page follows that printed on the
  2678. \\{recto} side, regardless of the page number; no blank pages appear, unless
  2679. explicitly generated by \TeX.
  2680.  
  2681. @<Determine the desired |duplex_by_page_numbers|@>=
  2682. duplex_by_page_numbers:=odd(VAX_cli_present('DUPLEX_BY_PAGE_NUMBERS'))
  2683.  
  2684. @ After the dialogue is over, we print the options so that the user
  2685. can see what \.{DVItoLN03} thought was specified.
  2686.  
  2687. These macros allow us to show which |print_mode| and feed tray have been
  2688. selected:
  2689.  
  2690. @d show_print_mode==begin
  2691. print('Print mode is ');
  2692. case print_mode of
  2693.   true_simplex_normal,true_duplex_normal,duplex_master_normal:
  2694.     print('normal ');
  2695.   true_simplex_tumbled,true_duplex_tumbled,duplex_master_tumbled:
  2696.     print('tumbled ')
  2697. endcases;
  2698. if print_mode=ln03_master then print('ln03 ');
  2699. if print_mode>=duplex_master_normal then print('master ');
  2700. if print_mode>=true_duplex_normal then
  2701.   print_ln('duplex')
  2702. else
  2703.   print_ln('simplex')
  2704. end
  2705. @#
  2706. @d show_tray(#)==case # of
  2707.   default_tray: print_ln('fed from the default tray');
  2708.   top_tray: print_ln('fed from the top tray');
  2709.   bottom_tray: print_ln('fed from the bottom tray');
  2710.   envelope_feeder: print_ln('taken from the envelope feeder');
  2711.   manual_feed: print_ln('manually fed')
  2712. endcases
  2713.  
  2714. @<Print all the selected options@>=
  2715. print_ln('Options selected:');
  2716. @.Options selected@>
  2717. print('  Starting page = ');
  2718. for k:=0 to start_vals do
  2719.   begin if start_there[k] then print(start_count[k]:1)
  2720.   else print('*');
  2721.   if k<start_vals then print('.')
  2722.   else print_ln(' ');
  2723.   end;
  2724. print_ln('  Maximum number of pages = ',max_pages:1);
  2725. print_ln('  Printing starts ',top_marg:1,' pixels from the top of the paper, and');
  2726. print_ln('    ',left_marg:1,' pixels from the left-hand edge');
  2727. print_ln('  Orientation = ',orientation);
  2728. print_ln('  Resolution = ',resolution:12:8,' pixels per inch');
  2729. if new_mag>0 then print_ln('  New magnification factor = ',new_mag/1000:8:3);
  2730. if h_fuzz>99 then print_ln('  Horizontal overruns less than ',
  2731.                             (h_fuzz+1)/65536.0:1:2,'pt will be ignored');
  2732. if v_fuzz>99 then print_ln('  Depth overruns less than ',
  2733.                             (v_fuzz+1)/65536.0:1:2,'pt will be ignored');
  2734. if (first_tray<>default_tray) or (following_tray<>default_tray) then
  2735.   if first_tray<>following_tray then
  2736.   begin
  2737.     print('First sheet is '); show_tray(first_tray);
  2738.     print('Following sheets are '); show_tray(following_tray)
  2739.   end
  2740.   else
  2741.   begin
  2742.     print('Paper is '); show_tray(first_tray)
  2743.   end;
  2744. if print_mode<>default_print_mode then
  2745.   show_print_mode;
  2746. if print_mode>=true_duplex_normal then
  2747. begin
  2748.   print('Duplexing is ');
  2749.   if not duplex_by_page_numbers then print('not ');
  2750.   print_ln('controlled by page numbers')
  2751. end
  2752.  
  2753. @* Defining fonts.
  2754. \.{DVItoLN03} reads the postamble first and loads
  2755. all of the fonts defined there; then it processes the pages. Therefore
  2756. a \\{fnt\_def} command should match a previous definition if and only
  2757. if the \\{fnt\_def} being processed is not in the postamble.
  2758.  
  2759.  
  2760. Since the ``wanted'' pages are read twice, {\it viz.} whilst gathering the
  2761. font usage statistics and to perform the actual type setting, font
  2762. definitions in the \.{DVI} file will actually be met {\bf three} times. The
  2763. original logic of \.{DVItype} using the global variable |in_postamble|
  2764. covers the first two occasions during the  initial scan and the reading of
  2765. the postamble.  The variable |substitution_pass| prevents us from getting a
  2766. ``font already defined'' message when each definition is met for the third
  2767. time.
  2768.  
  2769. @<Glob...@>=
  2770. @!substitution_pass:boolean; {are we performing the actual type setting?}
  2771. @!in_postamble:boolean; {are we reading the postamble?}
  2772.  
  2773. @ @<Set init...@>=
  2774. substitution_pass:=false;
  2775. in_postamble:=false;
  2776.  
  2777. @ Under the VAX/VMS operating system, no distinction is made between fonts
  2778. names \.{cmr10} and \.{CMR10}, for example.  However, if \TeX\ encounters
  2779. a font name not written in the same case as those of the preloaded
  2780. format in use, it will think it is a distinct font (and under some other
  2781. operating systems, of course, the case of the name could be significant).
  2782. The following function allows us to fold upper-case letters in a font name
  2783. to lower-case, and thus avoid such infelicitous duplication.
  2784.  
  2785. @p function lower(@!ch:ASCII_code): ASCII_code;
  2786. begin
  2787.   if (ch>="A") and (ch<="Z") then
  2788.     lower:=ch+"a"-"A"
  2789.   else
  2790.     lower:=ch
  2791. end;
  2792.  
  2793. @ The following subroutine does the necessary things when a \\{fnt\_def}
  2794. command is being processed.
  2795.  
  2796. @p procedure define_font(@!e:integer); {|e| is an external font number}
  2797. var @!f:0..max_fonts; {local index into font data structures}
  2798. @!p:integer; {length of the area/directory spec}
  2799. @!newly_defined: boolean; {first time font has been defined}
  2800. @!n:integer; {length of the font name proper}
  2801. @!c,@!q,@!d:integer; {check sum, scaled size, and design size}
  2802. @!r:0..name_length; {index into |cur_name|}
  2803. @!j,@!k:0..name_size; {indices into |names|}
  2804. @!mismatch:boolean; {do names disagree?}
  2805. begin if nf=max_fonts then capacity_exceeded('too many fonts (max fonts=',
  2806.                                              max_fonts:1,')');
  2807. @:capacity exceeded too many fonts}{\quad \.{too many fonts}@>
  2808. if e > highest_font then highest_font := e;
  2809. font_num[nf]:=e; f:=0; font_map[nf]:=nf;
  2810. while font_num[f]<>e do incr(f);
  2811. @<Read the font parameters into position for font |nf|, and
  2812.   print the font name@>;
  2813. if in_postamble then
  2814.   begin if (f<nf) and not substitution_pass then
  2815.     warning('---this font was already defined!');
  2816. @:Warning: this font was already defined}{\quad \.{this font was already defined}@>
  2817.   end
  2818. else  begin if f=nf then warning('---this font wasn''t loaded before!');
  2819. @:Warning: this font wasn't loaded before}{\quad \.{this font wasn't loaded before}@>
  2820.   end;
  2821. newly_defined:=f=nf;@/
  2822. @<Avoid reloading the font metrics, if identical font already present@>;
  2823. if f=nf then @<Load the new font, unless there are problems@>
  2824. else
  2825. begin
  2826.   @<Check that the current font definition matches the old one@>;
  2827.   if newly_defined and (font_map[nf]<nf) then
  2828.   begin
  2829.     print('---is identical to font ',font_num[f]:1);
  2830. @.is identical to font ...@>
  2831.     incr(nf) {the ``new'' font is officially present, but remapped}
  2832.   end
  2833. end;
  2834. end;
  2835.  
  2836. @ When we reach the point at which it might be found necessary to read the
  2837. \TeX\ font metrics from the \.{TFM} file, we can save a little effort if
  2838. we've already loaded a font with identical name and at the same scaled size.
  2839. In fact, this is essential with such virtual fonts as \.{recurse}, because
  2840. otherwise we might go on loading the same \.{TFM} file \\{ad infinitum}!
  2841. Variable |mismatch| is misused in the |while| loop such that the loop will
  2842. exit if the name of font |f| is identical to that of font |nf|.  If the
  2843. names differ, the loop continues until |f=nf|.
  2844.  
  2845. @<Avoid reloading the font metrics, if identical font already present@>=
  2846. f:=font_map[f]; {it might already map elsewhere!}
  2847. if f=nf then {it's got to \&{be} a new font, of course!}
  2848. begin
  2849.   f:=0; mismatch:=true;
  2850.   while (f<nf) and mismatch do
  2851.   begin
  2852.     if font_scaled_size[f]=q then {don't try name unless same size}
  2853.       @<Compare two font names, and set |mismatch| accordingly@>;
  2854.     if mismatch then incr(f)
  2855.   end; {|while f|}
  2856.   if f<nf then {the font already exists, under a different identity}
  2857.     font_map[nf]:=f;
  2858. end
  2859.  
  2860. @ If the \.{DVI} file has previously defined this font, we check that the
  2861. parameters of this |fnt_def| command match those previously read: there's
  2862. something amiss if they differ (and the user is supposed to determine what
  2863. by using \.{DVItype}).
  2864.  
  2865. @<Check that the current...@>=
  2866. begin if font_check_sum[f]<>c then
  2867.   warning('---checksum doesn''t match previous definition!');
  2868. @:Warning: checksum doesn't match}{\quad\.{checksum doesn't match}@>
  2869. if font_scaled_size[f]<>q then
  2870.   warning('---scaled size doesn''t match previous definition!');
  2871. @:Warning: scaled size doesn't match}{\quad\.{scaled size doesn't match}@>
  2872. if font_design_size[f]<>d then
  2873.   warning('---design size doesn''t match previous definition!');
  2874. @:Warning: design size doesn't match}{\quad\.{design size doesn't match}@>
  2875. @<Compare two font names, and set |mismatch| accordingly@>;
  2876. if mismatch then warning('---font name doesn''t match previous definition!');
  2877. @:Warning: font name doesn't match}{\quad\.{font name doesn't match}@>
  2878. end
  2879.  
  2880. @ This little chunk of code compares the font names stored in
  2881. |names[font_name[f]..font_name[f+1]-1]| and
  2882. |names[font_name[nf]..font_name[nf+1]-1]|; if any character differs, or the
  2883. lengths of the names differ, then |mismatch| will be set.
  2884.  
  2885. @<Compare two font names, and set |mismatch| accordingly@>=
  2886. begin
  2887.   j:=font_name[f]; k:=font_name[nf]; mismatch:=false;
  2888.   while j<font_name[f+1] do
  2889.   begin
  2890.     if names[j]<>names[k] then mismatch:=true;
  2891.     incr(j); incr(k);
  2892.   end; {|while j|}
  2893.   if k<>font_name[nf+1] then mismatch:=true
  2894. end
  2895. @ @<Read the font parameters into position for font |nf|...@>=
  2896. c:=signed_quad; font_check_sum[nf]:=c;@/
  2897. q:=signed_quad; font_scaled_size[nf]:=q;@/
  2898. d:=signed_quad; font_design_size[nf]:=d;@/
  2899. p:=get_byte; n:=get_byte;
  2900. if font_name[nf]+n+p>name_size then
  2901.   capacity_exceeded('too many names (name size=',name_size:1,')');
  2902. @:capacity exceeded too many names}{\quad\.{too many names}@>
  2903. font_name[nf+1]:=font_name[nf]+n+p;
  2904. if not substitution_pass then
  2905.     print('Font ',e:1,': ');
  2906. if n+p=0 then print('null font name!')
  2907. @.null font name@>
  2908. else for k:=font_name[nf] to font_name[nf+1]-1 do names[k]:=lower(get_byte);
  2909. incr(nf); if not substitution_pass then print_font(nf-1); decr(nf)
  2910.  
  2911. @ @<Load the new font, unless there are problems@>=
  2912. begin @<Move font name into the |cur_name| string@>;
  2913. open_tfm_file;
  2914. if (q<=0)or(q>=@'1000000000) then
  2915.     abort('Font not loaded, bad scale (',q:1,')!')
  2916. @:fatal error Font not loaded bad scale}{\qquad\.{bad scale}@>
  2917.   else if (d<=0)or(d>=@'1000000000) then
  2918.     abort('Font not loaded, bad design size (',d:1,')!')
  2919. @:fatal error Font not loaded bad design size}{\qquad\.{bad design size}@>
  2920.   else if in_TFM(q) then @<Finish loading the new font info@>;
  2921. end
  2922.  
  2923. @ Strictly speaking, we should derive the width of a `thin space' from
  2924. |param[6]| in the |tfm_file|, but this program (in common with such paragons
  2925. of virtue as \.{DVItype}!) doesn't bother to read that far in the file; so
  2926. we assume that \.{1em} in the font is equivalent to the design size, and
  2927. knowing that an \.{em} in the Computer Modern fonts is \.{18u\#} wide, take
  2928. one-sixth of this to represent the `thin space'.
  2929.  
  2930. @<Finish loading...@>=
  2931. begin font_space[nf]:=q div 6; {this is a 3-unit ``thin space''}
  2932. if (c<>0)and(tfm_check_sum<>0)and(c<>tfm_check_sum) then
  2933.   warning('checksum doesn''t match; DVI=',c:1,' vs. TFM=',tfm_check_sum:1);
  2934. @:Warning: checksum doesn\'t match}{\quad\.{checksum doesn't match}@>
  2935. print('---loaded at size ',q:1,' DVI units');
  2936. d:=round((100.0*conv*q)/(true_conv*d));
  2937. if d<>100 then
  2938.   print(' (this font is magnified ',d:1,'%)');
  2939. @.this font is magnified@>
  2940. incr(nf); {now the new font is officially present}
  2941. font_space[nf]:=0; {for |out_space| and |out_vmove|}
  2942. end
  2943.  
  2944. @ The string |cur_name| is supposed to be set to the external name of the
  2945. \.{TFM} file for the current font. This usually means that we need to
  2946. prepend the name of the default directory, and
  2947. to append the suffix `\.{.TFM}'. Furthermore, we change lower case letters
  2948. to upper case, since |cur_name| is a \PASCAL\ string.
  2949. If |p=0|, i.e., if no font directory has been specified, \.{DVItoLN03}
  2950. is supposed to use the default font directory, which is a
  2951. system-dependent place where the standard fonts are kept; in the current
  2952. implementation, this will have been read into |tfm_directory| from the
  2953. command-line qualifier \.{/font\UL directory}.
  2954. @^system dependencies@>
  2955.  
  2956. @<Move font name into the |cur_name| string@>=
  2957. for k:=1 to name_length do cur_name[k]:=' ';
  2958. if p=0 then
  2959.   begin for k:=1 to tfm_directory.length do
  2960.     cur_name[k]:=tfm_directory.body[k];
  2961.   r:=tfm_directory.length;
  2962.   end
  2963. else r:=0;
  2964. for k:=font_name[nf] to font_name[nf+1]-1 do
  2965.   begin incr(r);
  2966.   if r+4>name_length then
  2967.     capacity_exceeded('font file name too long (max length=',name_length:1,')');
  2968. @:capacity exceeded font file name}{\quad\.{font file name too long}@>
  2969.   if (names[k]>="a")and(names[k]<="z") then
  2970.       cur_name[r]:=xchr[names[k]-@'40]
  2971.   else cur_name[r]:=xchr[names[k]];
  2972.   end;
  2973. cur_name[r+1]:='.'; cur_name[r+2]:='T'; cur_name[r+3]:='F'; cur_name[r+4]:='M'
  2974.  
  2975. @* Accumulation of page.  We save a page before outputting it, to minimize the
  2976. complexity of the page for the LN03.
  2977.  
  2978. The page is stored as a series of linked lists of ``segments'', with
  2979. (potentially) one such list being stored for each pixel position in the
  2980. vertical direction.
  2981.  
  2982. The individual segments are a structure, containing the $x$-coordinates of the
  2983. start and finish of the text.  We also record the current font in which the
  2984. text is to be set, and a pointer to the next segment in the list.
  2985.  
  2986. We therefore declare here the structure to hold the segment, and an array of
  2987. pointers, one for each vertical pixel position.  We also provide a further two
  2988. segment pointers, one for the ``current'' segment, and another pointing to a
  2989. list of allocated, but otherwise unused, segments.  Thus it is only necessary
  2990. to call for storage allocation when the free list is exhausted.
  2991.  
  2992. If a glyph proved to have too large a bitmap for it to have been downloaded
  2993. as a character into the LN03, a segment was appended which references that
  2994. glyph alone; this is necessary to ensure that the bitmaps are only output
  2995. once the |eop| has been met, to accommodate changes of orientation brought
  2996. about by \.{\\special} commands.
  2997.  
  2998. @d max_text=max_print   {Largest text segment held in a record}
  2999.  
  3000. @<Types in the...@>=
  3001.   @!string  = varying [10] of char;
  3002.       {to hold character representations of integers}
  3003.   @!segment = packed record
  3004.           @!next : ^segment; {pointer to next segment, or |nil|}
  3005.           @!xs,@!xe : integer;  {$x$-coords of start and finish of text}
  3006.           @!font : integer; {LN03 downloaded font \#; external font for bitmaps}
  3007.           @!ch   : eight_bits; {character from LN03 internal font}
  3008.           @!seg_type : (@!glyph_string,@!bitmap,@!sixel_dump);
  3009.           @!direction : page_orientation; {|landscape| or |portrait|}
  3010.           @!text : varying [max_text] of char; {sequence for output to printer}
  3011.         end;
  3012.  
  3013. @ There is one |seg_list| pointer for each possible horizontal row of pixels
  3014. on the paper; |cur_seg| points to the current segment, whilst |free_list_seg|
  3015. maintains a list of free segments.  Any characters which would appear outside
  3016. the bounds of the paper are accumulated in segments of the |lost_seg| list.
  3017.  
  3018. @<Globals in the ...@>=
  3019.   @!seg_list : array [y_land_min..paper_ht+y_port_min] of ^segment;
  3020.   @!cur_seg, @!free_seg_list : ^segment;
  3021.   @!lost_seg : ^segment;        {For segments set off the paper}
  3022.   @!seg_max : integer; {could be used for statistics}
  3023.  
  3024. @ @<Set initial...@>=
  3025.   free_seg_list:=nil; cur_seg:=nil; lost_seg:=nil;
  3026.   for k:=y_land_min to paper_ht+y_port_min do seg_list[k]:=nil;
  3027.   seg_max:=0;
  3028.   top_of_page:=true;
  3029.  
  3030. @ The procedure |new_segment| allocates storage for a new segment.  The
  3031. program maintains a |free_seg_list| to which processed segments are returned;
  3032. however, if no free segments remain on this list (as will obtain on first use
  3033. of the program), the \PASCAL\ |new| function is used to allocate an
  3034. appropriate area.
  3035.  
  3036. After the segment has been allocated, as many fields as possible are
  3037. completed: (1) the |next| field points to the existing |seg_list| to which
  3038. this new segment now belongs; (2) the |font| field records the font currently
  3039. selected; (3) the |seg_list| is made to point to this new segment.  If the
  3040. text is known to be invalid (for the case in which it has been discovered that
  3041. the text will not fit on the physical paper), then the new segment is added
  3042. instead to the |lost_seg| list, and the |font| field is used to record the
  3043. $y$-position at which \TeX\ had tried to locate the text.
  3044.  
  3045. @p  procedure new_segment(@!valid,@!offset:boolean;
  3046.                           @!font_code,@!ch_code:integer);
  3047.   begin
  3048.     @<Find or create a new |cur_seg|@> ;
  3049.     @<Set fields of |cur_seg|@>
  3050.   end; {|new_segment|}
  3051.  
  3052. @ If the |free_seg_list| is empty, either we've never allocated any segments
  3053. before, or we've used them all.  We therefore use the standard \PASCAL\
  3054. allocator function, |new|.
  3055.  
  3056. Alternatively, we just detach the first segment from the |free_seg_list|.
  3057.  
  3058. @<Find or create a new |cur_seg|@>=
  3059. if free_seg_list = nil then
  3060. begin new(cur_seg);
  3061.   incr(seg_max)
  3062. end else
  3063. begin
  3064.   cur_seg:=free_seg_list;
  3065.   free_seg_list:=cur_seg^.next
  3066. end
  3067.  
  3068. @ If the character being set is correctly within the imaging area of the
  3069. paper, we add the new segment to the segment list associated with its vertical
  3070. position on the page, and record the |ln_font| in which the character shall be
  3071. output.
  3072.  
  3073. Alternatively, we add the segment to the list of |lost_seg|s, and use the
  3074. |font| field to record the vertical |y_pos| at which \TeX\ \\{thought} it
  3075. was imaging the text.
  3076.  
  3077. References to actual paper positions are always offset by the setting of
  3078. |left_marg| and |top_marg|.
  3079.  
  3080. @d x_pos==hh+left_marg          {Actual paper coordinates}
  3081. @d y_pos==vv+top_marg
  3082.  
  3083. @<Set fields of |cur_seg|@>=
  3084. with cur_seg^ do
  3085.   if valid then
  3086.   begin
  3087.     ch:=ch_code;
  3088.     direction:=new_orient;
  3089.     if offset then {oversized glyphs and sixel graphics inclusions}
  3090.     begin
  3091.       next:=seg_list[y_pos+bitmap_offset]; {Goes on `lower' list}
  3092.       seg_type:=bitmap; {this is corrected externally if necessary}
  3093.       font:=cur_font; {record index into fonts for later file opening}
  3094.       seg_list[y_pos+bitmap_offset]:=cur_seg    {Put at front of the list}
  3095.     end
  3096.     else
  3097.     begin
  3098.       next:=seg_list[y_pos];
  3099.       seg_type:=glyph_string;
  3100.       font:=font_code; {record number of a \\{downloaded} font}
  3101.       seg_list[y_pos]:=cur_seg  {Put at front of the list}
  3102.     end
  3103.   end else
  3104.   begin next:=lost_seg; {Don't put it on paper}
  3105.     font:=y_pos;                {Remember where \TeX\ wanted to put it}
  3106.     lost_seg:=cur_seg
  3107.   end
  3108.  
  3109.  
  3110. @ Procedure |finish_seg| is used to discard segments which record text which
  3111. was set off the paper, after making appropriate error indications in the log
  3112. (\.{TYP}) file.  Discarded segments are returned to the |free_seg_list|.
  3113.  
  3114. The procedure is also called when a glyph is met that cannot become a member
  3115. of that held in |cur_seg|; this just results in |cur_seg:=nil|.  The segment
  3116. is already attached to some part of |seg_list| array, so nothing further is
  3117. required, and nothing gets lost!
  3118.  
  3119. @p  procedure finish_seg;
  3120.   begin
  3121.     while (cur_seg<>nil) and (cur_seg=lost_seg) do
  3122.     begin                       {We've got something set off the paper}
  3123.       with cur_seg^ do
  3124.       begin
  3125.         warning('Characters ''',text,''' set off paper ');
  3126. @:Warning: Characters set off paper}{\quad\.{Characters ... set off paper}@>
  3127.         write_ln(term_out,'(see log file)',crlf);
  3128.         print_ln('( x = ',xs:1,'..',xe:1,', y = ',font:1,' ) - ignored');
  3129.         lost_seg:=next; next:=free_seg_list; free_seg_list:=cur_seg;
  3130.         cur_seg:=lost_seg
  3131.       end;
  3132.     end;
  3133.     cur_seg:=nil                {Forget that we've got a current segment}
  3134.   end;
  3135.  
  3136. @ The function |str_int| yields a minimum length ASCII representation for its
  3137. integer parameter |num|.  It does this by invoking the VAX-\PASCAL\ specific
  3138. function |dec(n,f,d)|, which yields an |f| character-long string containing
  3139. the decimal representation of the integer |n| occupying |d| digits at the
  3140. right-hand end of the string; if necessary leading `\.0' characters are
  3141. inserted to ensure that |d| digits are printed. (The leading |f-d| characters
  3142. of the string would be filled with the space character `\.\SP', but we
  3143. ensure that |f| and |d| are both just wide enough to avoid such a problem!)
  3144.  
  3145. @d VAX_decimal==@=dec@>
  3146.  
  3147. @p  function str_int(num : integer) : string;
  3148.   var field,digits : integer;
  3149.   begin if num = 0 then digits:=1 else
  3150.     digits:=trunc(ln(abs(num))/ln(10))+1;
  3151.     {$\bigl\lceil\log_{10}\vert\hbox{\\{num}}\vert\bigr\rceil$ is number of digits
  3152.     required}
  3153.     field:=digits;
  3154.     if num < 0 then incr(field);
  3155.     str_int:=VAX_decimal(num,field,digits)
  3156.   end;
  3157.  
  3158. @ We define here some non-printing control characters, which will later be
  3159. required for control and escape sequences.
  3160.  
  3161. @d LF==chr(@"0A)
  3162. @d FF==chr(@"0C)
  3163. @d esc==chr(@"1B)       {The ASCII ESCape character}
  3164. @d csi==chr(@"9B)       {Control Sequence Introducer}   
  3165. @d dcs==chr(@"90)       {Device Control String}
  3166. @d st==chr(@"9C)        {String Terminator}
  3167.  
  3168. @ The following |move_forward| procedure includes within the text of a segment
  3169. the appropriate LN03 |HPR| (Horizontal Position Relative) control sequence to
  3170. instruct the printer to move the printing position to the right.  This
  3171. sequence consists of the control sequence introducer, |csi|, followed by the
  3172. motion expressed in pixels, followed by the sequence terminator `\.a'.
  3173.  
  3174. @p  procedure move_forward(dist:integer);
  3175.   begin
  3176.     cur_seg^.text:=cur_seg^.text + csi+str_int(dist)+'a';
  3177.   end;
  3178.  
  3179. @ Because of the restrictions of \PASCAL, we have to make a |forward|
  3180. declaration of procedure |do_page|.  The procedure is called (recursively)
  3181. to interpret  the |dvi| bytes stored from a character packet in a virtual
  3182. font.  It also implements the normal operations for interpreting the
  3183. contents of a page read from the \.{DVI} file.
  3184.  
  3185. @p function do_page(@!vf_start:integer):boolean; forward;
  3186.  
  3187. @ Procedure |ord_text| adds a single character, |ch|, taken from the
  3188. |cur_font|, to a segment preparatory to its being ``set'' on the paper.  The
  3189. procedure cannot be declared at this point in the \.{WEB} source because it
  3190. references a number of procedures that haven't yet been declared. However,
  3191. we'll arrange for it to appear in the correct part of the \PASCAL\ later on.
  3192.  
  3193. If the character is taken from a virtual font, and requires more than the
  3194. simple operation of setting a single character from a single font, then this
  3195. procedure calls |do_page| recursively to interpret the sequence of |dvi|
  3196. bytes that have been stored into |vf| from the \.{VF} file.
  3197.  
  3198. This macro is equivalent to |glyph_map(cur_font)(#)|, assuming that
  3199. |cur_base=glyph_base[cur_font]|.
  3200.  
  3201. @d cur_font_glyph(#)==glyphs[cur_base+#]
  3202.  
  3203. @<Declare the |ord_text| Procedure@>=
  3204. procedure ord_text(ch:integer);
  3205.     var @!good : boolean;
  3206. begin
  3207.   with cur_font_glyph(ch) do
  3208.   begin
  3209.     @<Check if glyph appears within printable area@>;
  3210.     @<Allocate segment to hold text string@>;
  3211.     if loaded=virtual then
  3212.     begin
  3213.       finish_seg;       {Whatever precedes this character to be treated
  3214. separately}
  3215.       if not do_page(seq_off) then
  3216.         abort('VF packet failed [',cur_font:1,',',ch:1,']!');
  3217. @:fatal error VF packet failed}{\quad\.{VF packet failed}@>
  3218.       finish_seg;       {Don't run into next character or record this one's
  3219. width twice}
  3220.     end
  3221.     else begin
  3222.       @<Ensure entered text does not overflow |cur_seg|@>;
  3223.       @<Set character onto page@>
  3224.     end
  3225.   end
  3226. end {|procedure ord_text|}
  3227.  
  3228. @ If the character would be set off the physical paper addressable by the LN03
  3229. printer, the segment belongs to the |lost_seg| list.
  3230.  
  3231. @<Check if glyph appears within printable area@>=
  3232. if (x_pos<x_min) or (x_pos>page_wid) or (y_pos<y_min) or (y_pos>page_len) then
  3233. begin
  3234.   good := false;
  3235.   if (cur_seg<>nil) then
  3236.   begin
  3237.     if (cur_seg<>lost_seg) then
  3238.       finish_seg {earlier glyphs OK, even though this is off paper}
  3239.     else
  3240.       if cur_seg^.font<>y_pos then finish_seg {these were off paper, but on
  3241.                                                a different row}
  3242.   end;
  3243. end else@/
  3244. begin   {The character fits on the paper}
  3245.   good := true;
  3246.   if cur_seg<>nil then
  3247.   begin
  3248.     if cur_seg=lost_seg then finish_seg {report earlier off-paper chars}
  3249.     else
  3250.       if cur_seg^.seg_type=bitmap then finish_seg; {previous segment contains
  3251.                                                     single oversized glyph}
  3252.     if loaded=no then finish_seg {this segment will be ditto}
  3253.   end
  3254. end
  3255.  
  3256. @ If there is no |cur_seg| to hold the text, one is allocated, and the start and
  3257. finish coordinates of the text therein recorded.  The |text| field is set to be
  3258. the null string.
  3259.  
  3260. @<Allocate segment to hold text string@>=
  3261. if cur_seg = nil then
  3262. begin new_segment(good,loaded=no,font_code,ch);
  3263.   cur_seg^.xs:=x_pos;
  3264.   cur_seg^.xe:=x_pos;
  3265.   cur_seg^.text:=''
  3266. end
  3267.  
  3268. @ Checks are made that this segment (1) would not produce too large a
  3269. contiguous sequence of characters (which might therefore lead to an overlong
  3270. output record); (2) that the character does not need to be positioned to the
  3271. \&{left} of those already in the segment; (3) that the LN03 font used for this
  3272. character is the same as that for the other characters of the segment.  If any
  3273. of these checks fail, we commence a new segment; these tests are bypassed if
  3274. we're setting a single oversized glyph.
  3275.  
  3276. If any additional rightward movement is needed, the appropriate escape sequence
  3277. is added to the |text| in the |cur_seg|.
  3278.  
  3279. @<Ensure entered text does not overflow |cur_seg|@>=
  3280. if cur_seg^.seg_type=glyph_string then
  3281. begin
  3282.   if (VAX_length(cur_seg^.text)>=max_text-8) or
  3283.       (x_pos<cur_seg^.xe) or
  3284.       (font_code<>cur_seg^.font) then
  3285.   begin finish_seg;
  3286.     @<Allocate segment...@> {tests enumerated above failed}
  3287.   end
  3288.   else
  3289.   if x_pos>cur_seg^.xe then             {We need to move rightward}
  3290.     move_forward(x_pos-cur_seg^.xe)    {Add character to segment}
  3291. end
  3292.  
  3293. @ Characters whose glyphs proved too large to download in an LN03 font are
  3294. saved up to be printed as a pixel dump; other glyphs are recorded in the
  3295. current segment for imaging through the downloaded fonts.
  3296.  
  3297. If the character being stored is only being recorded to support generation
  3298. of an error message,  it is translated back from ASCII to the external
  3299. representation for printing of the message in the |type_file|.
  3300.  
  3301. @<Set character onto page@>=
  3302. if good then
  3303.   with cur_seg^ do
  3304.   case loaded of
  3305.     yes:
  3306.       text:=text+chr(char_code);
  3307.     no, missing:
  3308.       finish_seg;       {needn't consider further}
  3309.   othercases
  3310.     do_nothing;
  3311.   endcases
  3312. else
  3313.   cur_seg^.text:=cur_seg^.text+xchr[ch]
  3314.  
  3315.  
  3316. @* Packed file format.
  3317. This format was designed by Tomas Rokicki in August, 1985.
  3318. @^Rokicki, Tomas@>
  3319.  
  3320. It constitutes a compact representation of the data contained in a
  3321. \.{GF} file.  The information content is the same, but packed (\.{PK}) files
  3322. are almost always less than half the size of their \.{GF} counterparts.  They
  3323. are also easier to convert into a raster representation because they do not
  3324. have a profusion of \\{paint}, \\{skip}, and \\{new\_row} commands to be
  3325. separately interpreted.  In addition, the \.{PK} format expressedly forbids
  3326. \&{special} commands within a character.  The minimum bounding box for each
  3327. character is explicit in the format, and does not need to be scanned for as in
  3328. the \.{GF} format.  Finally, the width and escapement values are combined with
  3329. the raster information into character ``packets'', making it simpler in many
  3330. cases to process a character.
  3331.  
  3332. A \.{PK} file is organized as a stream of 8-bit bytes.  At times, these bytes
  3333. might be split into 4-bit nybbles or single bits, or combined into multiple
  3334. byte parameters.  When bytes are split into smaller pieces, the `first' piece
  3335. is always the most significant of the byte.  For instance, the first bit of
  3336. a byte is the bit with value 128; the first nybble can be found by dividing
  3337. a byte by 16.  Similarly, when bytes are combined into multiple byte
  3338. parameters, the first byte is the most significant of the parameter.  If the
  3339. parameter is signed, it is represented by two's-complement notation.
  3340.  
  3341. The set of possible eight-bit values are separated into two sets, those that
  3342. introduce a character definition, and those that do not.  The values that
  3343. introduce a character definition comprise the range from 0 to 239; byte values
  3344. above 239 are interpreted commands.  Bytes which introduce character
  3345. definitions are called flag bytes, and various fields within the byte indicate
  3346. various things about how the character definition is encoded.  Command bytes
  3347. have zero or more parameters, and can never appear within a character
  3348. definition or between parameters of another command, where they would be
  3349. interpeted as data.
  3350.  
  3351. A \.{PK} file consists of a preamble, followed by a sequence of one or more
  3352. character definitions, followed by a postamble.  The preamble command must
  3353. be the first byte in the file, followed immediately by its parameters.
  3354. Any number of character definitions may follow, and any command but the
  3355. preamble command and the postamble command may occur between character
  3356. definitions.  The very last command in the file must be the postamble.
  3357.  
  3358. @ The packed file format is intended to be easy to read and interpret by
  3359. device drivers.  The small size of the file reduces the input/output overhead
  3360. each time a font is defined.  For those drivers that load and save each font
  3361. file into memory, the small size also helps reduce the memory requirements.
  3362. The length of each character packet is specified, allowing the character raster
  3363. data to be loaded into memory by simply counting bytes, rather than
  3364. interpreting each command; then, each character can be interpreted on a demand
  3365. basis.  This also makes it possible for a driver to skip a particular
  3366. character quickly if it knows that the character is unused.
  3367.  
  3368. @ First, the command bytes shall be presented; then the format of the
  3369. Character definitions will be defined.  Eight of the possible sixteen
  3370. commands (values 240 through 255) are currently defined; the others are
  3371. reserved for future extensions.  The commands are listed below.  Each command
  3372. is specified by its symbolic name (e.g., \\{pk\_no\_op}), its opcode byte,
  3373. and any parameters.  The parameters are followed by a bracketed number
  3374. telling how many bytes they occupy, with the number preceded by a plus sign if
  3375. it is a signed quantity.  (Four byte quantities are always signed, however.)
  3376.  
  3377. \yskip\hang|pk_xxx1| 240 |k[1]| |x[k]|.  This command is undefined in general;
  3378. it functions as a $(k+2)$-byte \\{no\_op} unless special \.{PK}-reading
  3379. programs are being used.  \MF\ generates \\{xxx} commands when encountering
  3380. a \&{special} string.  It is recommended that |x| be a string having the form
  3381. of a keyword followed by possible parameters relevant to that keyword.
  3382.  
  3383. \yskip\hang\\{pk\_xxx2} 241 |k[2]| |x[k]|.  Like |pk_xxx1|, but |0<=k<65536|.
  3384.  
  3385. \yskip\hang\\{pk\_xxx3} 242 |k[3]| |x[k]|.  Like |pk_xxx1|, but
  3386. |0<=k<@t$2^{24}$@>|.  \MF\ uses this when sending a \&{special} string whose
  3387. length exceeds~255.
  3388.  
  3389. \yskip\hang\\{pk\_xxx4} 243 |k[4]| |x[k]|.  Like |pk_xxx1|, but |k| can be
  3390. ridiculously large; |k| musn't be negative.
  3391.  
  3392. \yskip\hang|pk_yyy| 244 |y[4]|.  This command is undefined in general; it
  3393. functions as a five-byte \\{no\_op} unless special \.{PK} reading programs
  3394. are being used.  \MF\ puts |scaled| numbers into |yyy|'s, as a result of
  3395. \&{numspecial} commands; the intent is to provide numeric parameters to
  3396. \\{xxx} commands that immediately precede.
  3397.  
  3398. \yskip\hang|pk_post| 245.  Beginning of the postamble.  This command is
  3399. followed by enough |pk_no_op| commands to make the file a multiple
  3400. of four bytes long.  Zero through three bytes are usual, but any number
  3401. is allowed.
  3402. This should make the file easy to read on machines which pack four bytes to
  3403. a word.
  3404.  
  3405. \yskip\hang|pk_no_op| 246.  No operation, do nothing.  Any number of
  3406. |pk_no_op|'s may appear between \.{PK} commands, but a |pk_no_op| cannot be
  3407. inserted between a command and its parameters, between two parameters, or
  3408. inside a character definition.
  3409.  
  3410. \yskip\hang|pk_pre| 247 |i[1]| |k[1]| |x[k]| |ds[4]| |cs[4]| |hppp[4]|
  3411. |vppp[4]|.  Preamble command.  Here, |i| is the identification byte of the
  3412. file, currently equal to 89.  The string |x| is merely a comment, usually
  3413. indicating the source of the \.{PK} file.  The parameters |ds| and |cs| are
  3414. the design size of the file in $1/2^{20}$ points, and the checksum of the
  3415. file, respectively.  The checksum should match the \.{TFM} file and the
  3416. \.{GF} files for this font.  Parameters |hppp| and |vppp| are the ratios
  3417. of pixels per point, horizontally and vertically, multiplied by $2^{16}$; they
  3418. can be used to correlate the font with specific device resolutions,
  3419. magnifications, and ``at sizes''.  Usually, the name of the \.{PK} file is
  3420. formed by concatenating the font name (e.g., amr10) with the resolution at
  3421. which the font is prepared in pixels per inch multiplied by the magnification
  3422. factor, and the letters \.{PK}.  For instance, amr10 at 300 dots per inch
  3423. should be named AMR10.300PK; at one thousand dots per inch and magstephalf,
  3424. it should be named AMR10.1095PK.
  3425.  
  3426. @ We put a few of the above opcodes into definitions for symbolic use by
  3427. this program.
  3428.  
  3429. @d pk_id = 89 {the version of \.{PK} file described}
  3430. @d pk_xxx1 = 240 {\&{special} commands}
  3431. @d pk_yyy = 244 {\&{numspecial} commands}
  3432. @d pk_post = 245 {postamble}
  3433. @d pk_no_op = 246 {no operation}
  3434. @d pk_pre = 247 {preamble}
  3435.  
  3436. @ The \.{PK} format has two conflicting goals; to pack character raster and
  3437. size information as compactly as possible, while retaining ease of translation
  3438. into raster and other forms.  A suitable compromise was found in the use of
  3439. run-encoding of the raster information.  Instead of packing the individual
  3440. bits of the character, we instead count the number of consecutive `black' or
  3441. `white' pixels in a horizontal raster row, and then encode this number.  Run
  3442. counts are found for each row, from the top of the character to the bottom.
  3443. This is essentially the way the \.{GF} format works.
  3444. Instead of presenting each row individually, however, let us concatenate all
  3445. of the horizontal raster rows into one long string of pixels, and encode this
  3446. row.  With knowledge of the width of the bit-map, the original character glyph
  3447. can be easily reconstructed.  In addition, we do not need special commands to
  3448. mark the end of one row and the beginning of the next.
  3449.  
  3450. Next, let us put the burden of finding the minimum bounding box on the part
  3451. of the font generator, since the characters will usually be used much more
  3452. often than they are generated.  The minimum bounding box is the smallest
  3453. rectangle which encloses all `black' pixels of a character.  Let us also
  3454. eliminate the need for a special end of character marker, by supplying
  3455. exactly as many bits as are required to fill the minimum bounding box, from
  3456. which the end of the character is implicit.
  3457.  
  3458. Let us next consider the distribution of the run counts.  Analysis of several
  3459. dozen pixel files at 300 dots per inch yields a distribution peaking at four,
  3460. falling off slowly until ten, then a bit more steeply until twenty, and then
  3461. asymptotically approaching the horizontal.  Thus, the great majority of our
  3462. run counts will fit in a four-bit nybble.  The eight-bit byte is attractive for
  3463. our run-counts, as it is the standard on many systems; however, the wasted four
  3464. bits in the majority of cases seems a high price to pay.  Another possibility
  3465. is to use a Huffman-type encoding scheme with a variable number of bits for
  3466. each run-count; this was rejected because of the overhead in fetching and
  3467. examining individual bits in the file.  Thus, the character raster definitions
  3468. in the \.{PK} file format are based on the four-bit nybble.
  3469.  
  3470. @ The analysis of the pixel files yielded another interesting statistic: fully
  3471. 37\char`\%\
  3472. of the raster rows were duplicates of the previous row.  Thus, the \.{PK}
  3473. format allows the specification of repeat counts, which indicate how many times
  3474. a horizontal raster row is to be repeated.  These repeated rows are taken out
  3475. of the character glyph before individual rows are concatenated into the long
  3476. string of pixels.
  3477.  
  3478. For elegance, we disallow a run count of zero.  The case of a null raster
  3479. description should be gleaned from the character width and height being equal
  3480. to zero, and no raster data should be read.  No other zero counts are ever
  3481. necessary.  Also, in the absence of repeat counts, the repeat value is set to
  3482. be zero (only the original row is sent.)  If a repeat count is seen, it takes
  3483. effect on the current row.  The current row is defined as the row on which the
  3484. first pixel of the next run count will lie.  The repeat count is set back to
  3485. zero when the last pixel in the current row is seen, and the row is sent out.
  3486.  
  3487. This poses a problem for entirely black and entirely white rows, however.  Let
  3488. us say that the current row ends with four white pixels, and then we have five
  3489. entirely empty rows, followed by a black pixel at the beginning of the next
  3490. row, and the character width is ten pixels.  We would like to use a repeat
  3491. count, but there is no legal place to put it.  If we put it before the white
  3492. run count, it will apply to the current row.  If we put it after, it applies
  3493. to the row with the black pixel at the beginning.  Thus, entirely white or
  3494. entirely black repeated rows are always packed as large run counts (in this
  3495. case, a white run count of 54) rather than repeat counts.
  3496.  
  3497. @ Now let us turn our attention to the actual packing of the run counts and
  3498. repeat counts into nybbles.  There are only sixteen possible nybble values.
  3499. We need to indicate run counts and repeat counts.  Since the run counts are
  3500. much more common, we will devote the majority of the nybble values to them.
  3501. We therefore indicate a repeat count by a nybble of 14 followed by a packed
  3502. number, where a packed number will be explained later.  Since the repeat
  3503. count value of one is so common, we indicate a repeat one command by a single
  3504. nybble of 15.  A 14 followed by the packed number 1 is still legal for a
  3505. repeat one count, however.  The run counts are coded directly as packed
  3506. numbers.
  3507.  
  3508. For packed numbers, therefore, we have the nybble values 0 through 13.  We
  3509. need to represent the positive integers up to, say, $2^{31}-1$.  We would
  3510. like the more common smaller numbers to take only one or two nybbles, and
  3511. the infrequent large numbers to take three or more.  We could therefore
  3512. allocate one nybble value to indicate a large run count taking three or more
  3513. nybbles.  We do this with the value 0.
  3514.  
  3515. @ We are left with the values 1 through 13.  We can allocate some of these, say
  3516. |dyn_f|, to be one-nybble run counts.
  3517. These will work for the run counts |1..dyn_f|.  For subsequent run
  3518. counts, we will use a nybble greater than |dyn_f|, followed by a second nybble,
  3519. whose value can run from 0 through 15.  Thus, the two-byte nybble values will
  3520. run from |dyn_f+1..(13-dyn_f)*16+dyn_f|.  We have our definition of large run
  3521. count values now, being all counts greater than |(13-dyn_f)*16+dyn_f|.
  3522.  
  3523. We can analyze our several dozen pixel files and determine an optimal value of
  3524. |dyn_f|, and use this value for all of the characters.  Unfortunately, values
  3525. of |dyn_f| that pack small characters well tend to pack the large characters
  3526. poorly, and values that pack large characters well are not efficient for the
  3527. smaller characters.  Thus, we choose the optimal |dyn_f| on a character basis,
  3528. picking the value which will pack each individual character in the smallest
  3529. number of nybbles.  Legal values of |dyn_f| run from 0 (with no one-byte run
  3530. counts) to 13 (with no two-byte run counts).
  3531.  
  3532. @ Our only remaining task in the coding of packed numbers is the large run
  3533. counts.  We use a scheme suggested by D.~E.~Knuth
  3534. @^Knuth, D.~E.@>
  3535. which will simply and elegantly represent arbitrarily large values.  The
  3536. general scheme to represent an integer |i| is to write its hexadecimal
  3537. representation, with leading zeros removed.  Then we count the number of
  3538. digits, and prepend one less than that many zeros before the hexadecimal
  3539. representation.  Thus, the values from one to fifteen occupy one nybble;
  3540. the values sixteen through 255 occupy three, the values 256 through 4095
  3541. require five, etc.
  3542.  
  3543. For our purposes, however, we have already represented the numbers one
  3544. through |(13-dyn_f)*16+dyn_f|.  In addition, the one-nybble values have
  3545. already been taken by our other commands, which means that only the values
  3546. from sixteen up are available to us for long run counts.  Thus, we simply
  3547. normalize our long run counts, by subtracting |(13-dyn_f)*16+dyn_f+1| and
  3548. adding 16, and then representing the result according to the scheme above.
  3549.  
  3550. @ The final algorithm for decoding the run counts based on the above scheme
  3551. might look like this, assuming a procedure called \\{pk\_nyb} is available
  3552. to get the next nybble from the file, and assuming that the global
  3553. |repeat_count| indicates whether a row needs to be repeated.  Note that this
  3554. routine is recursive, but since a repeat count can never directly follow
  3555. another repeat count, it can only be recursive to one level.
  3556.  
  3557. @<Packed number procedure@>=
  3558. function pk_packed_num : integer ;
  3559. var i, j, k : integer ;
  3560. begin
  3561.    i := get_nyb ;
  3562.    if i = 0 then begin
  3563.       repeat j := get_nyb ; incr(i) ; until j <> 0 ;
  3564.       while i > 0 do begin j := j * 16 + get_nyb ; decr(i) ; end ;
  3565.       pk_packed_num := j - 15 + (13-dyn_f)*16 + dyn_f ;
  3566.    end else if i <= dyn_f then
  3567.       pk_packed_num := i
  3568.    else if i < 14 then
  3569.       pk_packed_num := (i-dyn_f-1)*16+get_nyb+dyn_f+1
  3570.    else begin
  3571.       if i = 14 then
  3572.          repeat_count := pk_packed_num
  3573.       else
  3574.          repeat_count := 1 ;
  3575.       pk_packed_num := pk_packed_num ;
  3576.    end ;
  3577. end ;
  3578.  
  3579. @ For low resolution fonts, or characters with `gray' areas, run encoding can
  3580. often make the character many times larger.  Therefore, for those characters
  3581. that cannot be encoded efficiently with run counts, the \.{PK} format allows
  3582. bit-mapping of the characters.  This is indicated by a |dyn_f| value of
  3583. 14.  The bits are packed tightly, by concatenating all of the horizontal raster
  3584. rows into one long string, and then packing this string eight bits to a byte.
  3585. The number of bytes required can be calculated by |(width*height+7) div 8|.
  3586. This format should only be used when packing the character by run counts takes
  3587. more bytes than this, although, of course, it is legal for any character.
  3588. Any extra bits in the last byte should be set to zero.
  3589.  
  3590. @ At this point, we are ready to introduce the format for a character
  3591. descripter.  It consists of three parts: a flag byte, a character preamble,
  3592. and the raster data.  The most significant four nybbles of the flag byte
  3593. yield the |dyn_f| value for that character.  (Notice that only values of
  3594. 0 through 14 are legal for |dyn_f|, with 14 indicating a bit mapped character;
  3595. thus, the flag bytes do not conflict with the command bytes, whose upper nybble
  3596. is always 15.)  The next bit (with weight 16) indicates whether the first run
  3597. count is a black count or a white count, with a one indicating a black count.
  3598. For bit-mapped characters, this bit should be set to a zero.  The next bit
  3599. (with weight 8) indicates whether certain later parameters (referred to as size
  3600. parameters) are given in one-byte or two-byte quantities, with a one indicating
  3601. that they are in two-byte quantities.  The last two bits are concatenated on to
  3602. the beginning of the length parameter in the character preamble, which will be
  3603. explained below.
  3604.  
  3605. However, if the last three bits of the flag byte are all set (normally
  3606. indicating that the size parameters are two-byte values and that a 3 should be
  3607. prepended to the length parameter), then a long format of the character
  3608. preamble should be used instead of one of the short forms.
  3609.  
  3610. Therefore, there are three formats for the character preamble, and which one
  3611. is used depends on the least significant three bits of the flag byte.  If the
  3612. least significant three bits are in the range zero through three, the short
  3613. format is used.  If they are in the range four through six, the extended short
  3614. format is used.  Otherwise, if the least significant bits are all set, then
  3615. the long form of the character preamble is used.  The preamble formats are
  3616. explained below.
  3617.  
  3618. \yskip\hang Short form: |flag[1]| |pl[1]| |cc[1]| |tfm[3]| |dm[1]| |w[1]|
  3619. |h[1]| |hoff[+1]| |voff[+1]|.
  3620. If this format of the character preamble is used, the above
  3621. parameters must all fit in the indicated number of bytes, signed or unsigned
  3622. as indicated.  Almost all of the standard \TeX\ font characters fit; the few
  3623. exceptions are fonts such as \.{aminch}.
  3624.  
  3625. \yskip\hang Extended short form: |flag[1]| |pl[2]| |cc[1]| |tfm[3]| |dm[2]|
  3626. |w[2]| |h[2]| |hoff[+2]| |voff[+2]|.  Larger characters use this extended
  3627. format.
  3628.  
  3629. \yskip\hang Long form: |flag[1]| |pl[4]| |cc[4]| |tfm[4]| |dx[4]| |dy[4]|
  3630. |w[4]| |h[4]| |hoff[4]| |voff[4]|.  This is the general format which
  3631. allows all of the
  3632. parameters of the \.{GF} file format, including vertical escapement.
  3633. \vskip\baselineskip
  3634. The |flag| parameter is the flag byte.  The parameter |pl| (packet length)
  3635. contains the offset
  3636. of the byte following this character descripter, with respect to the beginning
  3637. of the |tfm| width parameter.  This is given so a \.{PK} reading program can,
  3638. once it has read the flag byte, packet length, and character code (|cc|), skip
  3639. over the character by simply reading this many more bytes.  For the two short
  3640. forms of the character preamble, the last two bits of the flag byte should be
  3641. considered the two most-significant bits of the packet length.  For the short
  3642. format, the true packet length might be calculated as |(flag mod 4)*256+pl|;
  3643. for the extended format, it might be calculated as |(flag mod 4)*65536+pl|.
  3644.  
  3645. The |w| parameter is the width and the |h| parameter is the height in pixels
  3646. of the minimum bounding box.  The |dx| and |dy| parameters are the horizontal
  3647. and vertical escapements, respectively.  In the short formats, |dy| is assumed
  3648. to be zero and |dm| is |dy| but in pixels;
  3649. in the long format, |dx| and |dy| are both
  3650. in pixels multiplied by $2^{16}$.  The |hoff| is the horizontal offset from the
  3651. upper left pixel to the reference pixel; the |voff| is the vertical offset.
  3652. They are both given in pixels, with right and down being positive.  The
  3653. reference pixel is the pixel which occupies the unit square in \MF; the
  3654. \MF\ reference point is the lower left hand corner of this pixel.  (See the
  3655. example below.)
  3656.  
  3657. @ \TeX\ requires that all characters which have the same character codes
  3658. modulo 256 also have the same |tfm| widths, and escapement values.  The \.{PK}
  3659. format does not itself make this a requirement, but in order for the font to
  3660. work correctly with the \TeX\ software, this constraint should be observed.
  3661. The current version of \TeX\ (1.5) cannot output character codes greater
  3662. than 255 anyway.
  3663.  
  3664. Following the character preamble is the raster information for the
  3665. character, packed by run counts or by bits, as indicated by the flag byte.
  3666. If the character is packed by run counts and the required number of nybbles
  3667. is odd, then the last byte of the raster description should have a zero
  3668. for its least significant nybble.
  3669.  
  3670. @ As an illustration of the \.{PK} format, the character \char4\ from the font
  3671. amr10 at 300 dots per inch will be encoded.  This character was chosen
  3672. because it illustrates some
  3673. of the borderline cases.  The raster for the character looks like this (the
  3674. row numbers are chosen for convenience, and are not \MF's row numbers.)
  3675.  
  3676. \vskip\baselineskip
  3677. \centerline{\vbox{\baselineskip=10pt
  3678. \halign{\hfil#\quad&&\hfil#\hfil\cr
  3679. 0& & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M\cr
  3680. 1& & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M\cr
  3681. 2& & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M\cr
  3682. 3& & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M\cr
  3683. 4& & &M&M& & & & & & & & & & & & & & & & &M&M\cr
  3684. 5& & &M&M& & & & & & & & & & & & & & & & &M&M\cr
  3685. 6& & &M&M& & & & & & & & & & & & & & & & &M&M\cr
  3686. 7\cr
  3687. 8\cr
  3688. 9& & & & &M&M& & & & & & & & & & & & &M&M& & \cr
  3689. 10& & & & &M&M& & & & & & & & & & & & &M&M& & \cr
  3690. 11& & & & &M&M& & & & & & & & & & & & &M&M& & \cr
  3691. 12& & & & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M& & \cr
  3692. 13& & & & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M& & \cr
  3693. 14& & & & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M& & \cr
  3694. 15& & & & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M& & \cr
  3695. 16& & & & &M&M& & & & & & & & & & & & &M&M& & \cr
  3696. 17& & & & &M&M& & & & & & & & & & & & &M&M& & \cr
  3697. 18& & & & &M&M& & & & & & & & & & & & &M&M& & \cr
  3698. 19\cr
  3699. 20\cr
  3700. 21\cr
  3701. 22& & &M&M& & & & & & & & & & & & & & & & &M&M\cr
  3702. 23& & &M&M& & & & & & & & & & & & & & & & &M&M\cr
  3703. 24& & &M&M& & & & & & & & & & & & & & & & &M&M\cr
  3704. 25& & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M\cr
  3705. 26& & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M\cr
  3706. 27& & &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M\cr
  3707. 28&*& &M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M&M\cr
  3708. &\hphantom{M}&\hphantom{M}\cr
  3709. }}}
  3710. The width of the minimum bounding box for this character is 20; its height
  3711. is 29.  The `*' represents the reference pixel; notice how it lies outside the
  3712. minimum bounding box.  The |hoff| value is $-2$, and the |voff| is~28.
  3713.  
  3714. The first task is to calculate the run counts and repeat counts.  The repeat
  3715. counts are placed at the first transition (black to white or white to black)
  3716. in a row, and are enclosed in brackets.  White counts are enclosed in
  3717. parentheses.  It is relatively easy to generate the counts list:
  3718. \vskip\baselineskip
  3719. \centerline{82 [2] (16) 2 (42) [2] 2 (12) 2 (4) [3]}
  3720. \centerline{16 (4) [2] 2 (12) 2 (62) [2] 2 (16) 82}
  3721. \vskip\baselineskip
  3722. Note that any duplicated rows that are not all white or all black are removed
  3723. before the repeat counts are calculated.  The rows thus removed are rows 5, 6,
  3724. 10, 11, 13, 14, 15, 17, 18, 23, and 24.
  3725.  
  3726. @ The next step in the encoding of this character is to calculate the optimal
  3727. value of |dyn_f|.  The details of how this calculation is done are not
  3728. important here; suffice it to say that there is a simple algorithm which in one
  3729. pass over the count list can determine the best value of |dyn_f|.  For this
  3730. character, the optimal value turns out to be 8 (atypically low).  Thus, all
  3731. count values less than or equal to 8 are packed in one nybble; those from
  3732. nine to $(13-8)*16+8$ or 88 are packed in two nybbles.  The run encoded values
  3733. now become (in hex, separated according to the above list):
  3734. \vskip\baselineskip
  3735. \centerline{\tt D9 E2 97 2 B1 E2 2 93 2 4 E3}
  3736. \centerline{\tt 97 4 E2 2 93 2 C5 E2 2 97 D9}
  3737. \vskip\baselineskip\noindent
  3738. which comes to 36 nybbles, or 18 bytes.  This is shorter than the 73 bytes
  3739. required for the bit map, so we use the run count packing.
  3740.  
  3741. @ The short form of the character preamble is used because all of the
  3742. parameters fit in their respective lengths.  The packet length is therefore
  3743. 18 bytes for the raster, plus
  3744. eight bytes for the character preamble parameters following the character
  3745. code, or 26.  The |tfm| width for this character is 640796, or {\tt 9C71C} in
  3746. hexadecimal.  The horizontal escapement is 25 pixels.  The flag byte is
  3747. 88 hex, indicating the short preamble, the black first count, and the
  3748. |dyn_f| value of 8.  The final total character packet, in hexadecimal, is:
  3749. \vskip\baselineskip
  3750. $$\vbox{\halign{\hfil #\quad&&{\tt #\ }\cr
  3751. Flag byte&88\cr
  3752. Packet length&1A\cr
  3753. Character code&04\cr
  3754. |tfm| width&09&C7&1C\cr
  3755. Horizontal escapement (pixels)&19\cr
  3756. Width of bit map&14\cr
  3757. Height of bit map&1D\cr
  3758. Horizontal offset (signed)&FE\cr
  3759. Vertical offset&1C\cr
  3760. Raster data&D9&E2&97\cr
  3761. &2B&1E&22\cr
  3762. &93&24&E3\cr
  3763. &97&4E&22\cr
  3764. &93&2C&5E\cr
  3765. &22&97&D9\cr}}$$
  3766.  
  3767. @* Routines to access the packed pixel files.  We shall require routines to
  3768. access the packed file; sometimes this may even require access to individual
  3769. \&{bits}, but mostly we shall be accessing bytes or nybbles.
  3770.  
  3771. We also need a function that will get a single byte from the \.{PK} file.
  3772. Again, buffering may be done in this procedure.
  3773.  
  3774. @p function pk_byte : eight_bits ;
  3775. var temp : eight_bits ;
  3776. begin
  3777.    temp := pxl_file^[pk_loc mod VAX_block_length] ;
  3778.    pk_loc := pk_loc + 1 ;
  3779.    if (pk_loc mod VAX_block_length)=0 then
  3780.         get(pxl_file) ;
  3781.    pk_byte := temp ;
  3782. end ;
  3783.  
  3784. @ As we are reading the packed file, we often need to fetch (signed or
  3785. unsigned) 16 and 32 bit quantities.  Here we have three procedures to do this.
  3786.  
  3787. @p function get_16 : integer ;
  3788. var a : integer ;
  3789. begin a := pk_byte ; get_16 := a * 256 + pk_byte ; end ;
  3790. @#
  3791. function get_16_signed : integer ;
  3792. var a : integer ;
  3793. begin a := get_16; if a>32767 then a := a-65536;
  3794.   get_16_signed := a ; end ;
  3795. @#
  3796. function get_32 : integer ;
  3797. var a : integer ;
  3798. begin a := get_16 ; if a > 32767 then a := a - 65536 ;
  3799. get_32 := a * 65536 + get_16 ; end ;
  3800.  
  3801. @ Now we read and check the preamble of the pk file.  In the preamble, we
  3802. find the |hppp|, |dsize|, |checksum|.  After we've read the preamble, no
  3803. character locators will be available.
  3804.  
  3805. @d two_to_the_20th==@"100000
  3806.  
  3807. @<Read preamble@>=
  3808. pk_loc:=0; char_num:=-1; {Ensure that |char_num| is ``undefined''}
  3809. if pk_byte <> pk_pre then abort('Bad pk file,  pre command missing') ;
  3810. @:fatal error Bad pk file ...}{\quad\.{Bad pk file}@>
  3811. @:fatal error Bad pk file pre command missing}{\qquad\\{pre }\.{command missing}@>
  3812. if pk_byte <> pk_id then abort('Bad pk file, wrong version') ;
  3813. @:fatal error Bad pk file wrong version}{\qquad\.{wrong version}@>
  3814. pk_j := pk_byte ;
  3815. for pk_i := 1 to pk_j do dummy:=pk_byte ;       { Skip over PK file comment }
  3816. dsize := get_32 ;
  3817. checksum := get_32 ;
  3818. hppp := get_32 ; vppp := get_32 ;
  3819. magnification := round(hppp * 72.27 / 65536) ; {|* 5| in PKtoPX}
  3820. if hppp <> vppp then warning('PK font pixels non-square');
  3821. @:Warning: PK font pixels non-square}{\quad\.{PK font pixels non-square}@>
  3822. dsize:=(dsize/two_to_the_20th)*(magnification/resolution);
  3823. for pk_i:=0 to 255 do ch_loc[pk_i]:=0;
  3824.  
  3825. @ Of course, we need to define the above variables.
  3826.  
  3827. @<Glob...@>=
  3828. @!checksum : integer ; {checksum of pixel file}
  3829. @!hppp, @!vppp : integer ; {horizontal and vertical points per inch}
  3830. @!pk_loc : integer ; { byte currently being accessed in |pk_file| }
  3831. @!dummy : integer ; { used to ``absorb'' unwanted bytes from |pk_file| }
  3832.  
  3833. @* Character unpacking.
  3834. This is the procedure where we read the character packet and decode it into
  3835. standard pixel raster format; if a glyph is encountered that is not used in the
  3836. present document, its packet is skipped.  We create one row at a time, checking
  3837. for repeat
  3838. commands, and then repeat the row as many times as is necessary. The information
  3839. gleaned from the beginning of the character packet is to be placed in various
  3840. local variables. We also check that the length of the character packet is
  3841. correct.
  3842.  
  3843. If the character's raster is null (as is the case with the invisible fonts used
  3844. by Sli\TeX), the |num_cols| and |num_rows| are set to suitable dummy values (see
  3845. the section $\langle$Complete remainder of Character Definition
  3846. parameters$\rangle$ for further information).
  3847.  
  3848. @<Unpack and write character@>=
  3849. dyn_f := flag_byte div 16 ;
  3850. flag_byte := flag_byte mod 16 ;
  3851. turn_on := flag_byte >= 8 ;
  3852. if turn_on then flag_byte := flag_byte - 8 ;
  3853. if flag_byte = 7 then
  3854.    @<Read long character preamble@>
  3855. else if flag_byte > 3 then
  3856.    @<Read extended short character preamble@>
  3857. else
  3858.    @<Read short character preamble@> ;@/
  3859. byte_width:=(num_cols+7) div 8; {Bytes required per row in |lnf_file|}
  3860. ch_loc[char_num]:=end_of_packet-packet_length;
  3861. with glyph_map(TeX_font)(char_num) do
  3862.   if loaded=wanted then
  3863.   begin
  3864.      base:=ras_beg+ras_len;@/
  3865.      @<Process |tfm_width| information@>;
  3866.      @<Validate size of character's glyph@>;
  3867.      @<Complete remainder of Character Definition parameters@>;
  3868.      @<Read and translate raster description@>;
  3869.      @<Calculate the number of bytes of rasters transferred@>;
  3870.      @<Complete the entry in the font character directory@>
  3871.   end
  3872.   else
  3873.     @<Skip through unwanted raster@> ;
  3874. if end_of_packet <> pk_loc then abort('Bad pk file!  Bad packet length.')
  3875. @.Bad pk file@>
  3876.  
  3877. @ A simplified version of this code is also used when printing the bitmap of
  3878. an oversized glyph in sixels.
  3879.  
  3880. @<Unpack and store character@>=
  3881. dyn_f := flag_byte div 16 ;
  3882. flag_byte := flag_byte mod 16 ;
  3883. turn_on := flag_byte >= 8 ;
  3884. if turn_on then flag_byte := flag_byte - 8 ;
  3885. if flag_byte = 7 then
  3886.    @<Read long character preamble@>
  3887. else if flag_byte > 3 then
  3888.    @<Read extended short character preamble@>
  3889. else
  3890.    @<Read short character preamble@> ;@/
  3891. byte_width:=(num_cols+7) div 8; {Bytes required per row in |lnf_file|}
  3892. ch_loc[char_num]:=end_of_packet-packet_length;
  3893. if char_num=ch then
  3894. begin
  3895.    base:=ras_beg+ras_len;@/
  3896.    @<Read and translate raster description@>;
  3897. end
  3898. else
  3899.    @<Skip through unwanted raster@> ;
  3900. if end_of_packet <> pk_loc then abort('Bad pk file!  Bad packet length.')
  3901. @.Bad pk file@>
  3902.  
  3903. @ We need a whole lot of globals used but not defined up there.  Some of these
  3904. have the same names as certain fields of |pxl_dir_type|, or of local variables
  3905. of procedures |add_txf_to_lnf| and |copy_char|.  This is in order that the
  3906. segments of \.{WEB} code invoked above reference these globals therein, but the
  3907. appropriate local variables when within those procedures (when being used for
  3908. \.{PXL} files).
  3909.  
  3910. @<Glob...@>=
  3911. @!pk_i, @!pk_j : integer ; {index pointers}
  3912. @!flag_byte : integer ; {the byte that introduces the character definition}
  3913. @!end_of_packet : integer ; {where we expect the end of the packet to be}@/
  3914. {Following are also names of fields in a record of type |pxl_dir_type|}@/
  3915. @!num_cols, @!num_rows : sixteen_bits; {width and height of character}
  3916. @!x_off, @!y_off : signed_word ; {$x$ and $y$ offsets of character}
  3917. @!tfm_width : integer ; {character tfm width}
  3918. @#
  3919. @!tfms : array [0..255] of integer ; {character tfm widths}
  3920. @!dx, @!dy : integer ; {escapement values}
  3921. @!dxs, @!dys : array [0..255] of integer ; {escapement values}
  3922. @!ch_loc : array[0..255] of integer; {has the character been seen (and where)?}
  3923. @!dyn_f : integer ; {dynamic packing variable}
  3924. @!char_num : integer ; {the character we are reading}
  3925. @!packet_length : integer ; {the length of the character packet}
  3926. @!base : integer ; {index into |lnf_bytes|}
  3927. @!to_where : integer ; {\&{cf.} |to_where| in |copy_char|}
  3928. @!len : integer ; {\&{cf.} |len| in |copy_char|}
  3929. @!byte_width : integer ; {Bytes per row in the |lnf_file| bitmap}
  3930.  
  3931. @ To skip over the raster of an unwanted character, we can simply jump forward
  3932. in the PK file to |end_of_packet|.
  3933.  
  3934. @<Skip through unwanted raster@>=
  3935. while pk_loc<end_of_packet do dummy := pk_byte;
  3936.  
  3937. @ Now, the preamble reading modules.  First, we have the general case: the
  3938. long character preamble format.
  3939.  
  3940. @<Read long character preamble@>=
  3941. begin
  3942.    packet_length := get_32 ; char_num := get_32 ;
  3943.    end_of_packet := packet_length + pk_loc ;
  3944.    packet_length := packet_length + 9 ;
  3945.    tfm_width := get_32 ;
  3946.    dx := get_32 ; dy := get_32 ;
  3947.    num_cols := get_32 ;
  3948.    num_rows := get_32 ;
  3949.    x_off := get_32 ; y_off := get_32 ;
  3950. end
  3951.  
  3952. @ This module reads the character preamble with double byte parameters.
  3953.  
  3954. @<Read extended short character preamble@>=
  3955. begin
  3956.    packet_length := (flag_byte - 4) * 65536 + get_16 ;
  3957.    char_num := pk_byte ;
  3958.    end_of_packet := packet_length + pk_loc ;
  3959.    packet_length := packet_length + 4 ;
  3960.    pk_i := pk_byte ;
  3961.    tfm_width := pk_i * 65536 + get_16 ;
  3962.    dx := get_16 * 65536 ;
  3963.    dy := 0 ;
  3964.    num_cols := get_16 ;
  3965.    num_rows := get_16 ;
  3966.    x_off := get_16_signed ; y_off := get_16_signed ;
  3967. end
  3968.  
  3969. @ Here we read the most common character preamble, that with single byte
  3970. parameters.
  3971.  
  3972. @<Read short character preamble@>=
  3973. begin
  3974.    packet_length := flag_byte * 256 + pk_byte ;
  3975.    char_num := pk_byte ;
  3976.    end_of_packet := packet_length + pk_loc ;
  3977.    packet_length := packet_length + 3 ;
  3978.    pk_i := pk_byte ;
  3979.    tfm_width := pk_i * 65536 + get_16 ;
  3980.    dx := pk_byte * 65536 ;
  3981.    dy := 0 ;
  3982.    num_cols := pk_byte ;
  3983.    num_rows := pk_byte ;
  3984.    x_off := pk_byte ; y_off := pk_byte ;
  3985.    if x_off > 127 then x_off := x_off - 256 ;
  3986.    if y_off > 127 then y_off := y_off - 256 ;
  3987. end
  3988.  
  3989. @ Now we have the most important part of unravelling packed pixel files, where
  3990. we actually interpret the commands in the raster description.  First of all,
  3991. we need a procedure to get a single nybble from the file, as well as one to
  3992. get a single bit.
  3993.  
  3994. We also are now able to ``declare'' |pk_packed_num|.
  3995.  
  3996. @p function get_nyb : integer ;
  3997. var temp : eight_bits ;
  3998. begin
  3999.    if bit_weight = 0 then begin
  4000.       input_byte := pk_byte ;
  4001.       bit_weight := 16 ;
  4002.    end ;
  4003.    temp := input_byte div bit_weight ;
  4004.    input_byte := input_byte - temp * bit_weight ;
  4005.    bit_weight := bit_weight div 16 ;
  4006.    get_nyb := temp ;
  4007. end ;
  4008. @#
  4009. function get_bit : boolean ;
  4010. var temp : boolean ;
  4011. begin
  4012.    bit_weight := bit_weight div 2 ;
  4013.    if bit_weight = 0 then begin
  4014.       input_byte := pk_byte ;
  4015.       bit_weight := 128 ;
  4016.    end ;
  4017.    temp := input_byte >= bit_weight ;
  4018.    if temp then
  4019.       input_byte := input_byte - bit_weight ;
  4020.    get_bit := temp ;
  4021. end ;
  4022. @#
  4023. @t\4@>@<Packed number procedure@>
  4024.  
  4025. @ Now, the globals to help communication between these procedures.
  4026.  
  4027. @<Glob...@>=
  4028. @!input_byte : eight_bits ; {the byte we are currently decimating}
  4029. @!bit_weight : eight_bits ; {weight of the current bit}
  4030. @!nybble : eight_bits ; {the current nybble}
  4031. @!row_addr : integer ; {whereabouts in |lnf_file| of start of each pixel row}
  4032. @!bitmap_start : integer ; {and of start of \&{first} pixel row}
  4033.  
  4034. @ And the main procedure.
  4035.  
  4036. @<Read and translate raster description@>=
  4037. bit_weight := 0 ; row_addr:=base; {Note where row of pixels starts}
  4038. bitmap_start:=base;
  4039. if dyn_f = 14 then
  4040.    @<Get raster by bits@>
  4041. else @<Create normally packed raster@>
  4042.  
  4043. @ If |dyn_f=14|, then we need to get the raster representation
  4044. one bit at a time.
  4045.  
  4046. @<Get raster by bits@>=
  4047. begin
  4048. for pk_i := 1 to num_rows do begin
  4049.    bite:=0; bite_weight:=7;
  4050.    for pk_j := 1 to num_cols do begin
  4051.       if get_bit then bite:=bite+power[bite_weight];
  4052.       decr(bite_weight);
  4053.       if bite_weight<0 then begin
  4054.          bite_weight:=7;
  4055.          put_lnf(bite);
  4056.          bite:=0
  4057.       end
  4058.    end;
  4059.    if bite_weight<7 then put_lnf(bite)
  4060. end;
  4061. end
  4062.  
  4063. @ We need the |power| array, which contains powers of two, as well as the |bite|
  4064. variable (which accumulates eight pixels) and |bite_weight|, which directs which
  4065. pixel is to be ``blackened''.  The array |gpower| contains 0..8 black pixels,
  4066. staring at the left-hand end.
  4067.  
  4068. @<Globals...@>=
  4069. @!bite : integer; {accumulates 8 pixels to go to |lnf_file|}
  4070. @!bite_weight : integer; {next bit of |bite| to be filled (leftmost=0)}
  4071. @!power:array [0..7] of integer; {|power[i]| contains $2^{7-i}$}
  4072. @!gpower:array [0..8] of integer; {|gpower[i]| contains ones in leftmost |i| bits}
  4073.  
  4074. @ @<Set initial...@>=
  4075. power[0]:=128;
  4076. for i:=1 to 7 do power[i]:=power[i-1] div 2;
  4077. gpower[0]:=0;
  4078. for i:=1 to 8 do gpower[i]:=gpower[i-1]+power[i-1];
  4079.  
  4080. @ Otherwise, we translate the bit counts into the raster rows.  |run_ct|
  4081. contains the number of bits of the current color, and |turn_on| indicates
  4082. whether or not they should be black.  |rows_left| contains the number of
  4083. rows to be sent.
  4084.  
  4085. @<Create normally packed raster@>=
  4086. begin
  4087. rows_left := num_rows ;
  4088. h_bit := num_cols ;
  4089. repeat_count := 0 ;
  4090. bite:=0; bite_weight:=8;
  4091. while rows_left > 0 do begin
  4092.    run_ct := pk_packed_num ;
  4093.    while run_ct>0 do begin
  4094.       if (run_ct<bite_weight) and (run_ct<h_bit) then begin
  4095.          if turn_on then
  4096.             bite:=bite+gpower[bite_weight]-gpower[bite_weight-run_ct];
  4097.          h_bit:=h_bit-run_ct;
  4098.          bite_weight:=bite_weight-run_ct;
  4099.          run_ct:=0
  4100.       end
  4101.       else if (run_ct>=h_bit) and (h_bit<=bite_weight) then begin
  4102.          if turn_on then
  4103.             bite:=bite+gpower[bite_weight]-gpower[bite_weight-h_bit];
  4104.          put_lnf(bite); bite:=0;
  4105.          for pk_i:=1 to repeat_count do
  4106.             for pk_j:=1 to byte_width do repeat_row;
  4107.          row_addr:=base;
  4108.          rows_left := rows_left - repeat_count - 1 ;
  4109.          repeat_count := 0 ;
  4110.          bite:=0; bite_weight:=8;
  4111.          run_ct := run_ct - h_bit ;
  4112.          h_bit := num_cols ;
  4113.       end
  4114.       else begin
  4115.          if turn_on then bite:=bite+gpower[bite_weight];
  4116.          put_lnf(bite);
  4117.          run_ct:=run_ct-bite_weight;
  4118.          h_bit:=h_bit-bite_weight;
  4119.          bite:=0; bite_weight:=8
  4120.       end
  4121.    end;
  4122.    turn_on := not turn_on ;
  4123. end ;
  4124. if (rows_left <> 0) or (h_bit <> num_cols) then
  4125.    abort('Bad pk file---more bits than required!');
  4126. @.Bad pk file@>
  4127. end
  4128.  
  4129. @ We need to declare the repeat flag, bit counter, and color flag here.
  4130.  
  4131. @<Glob...@>=
  4132. @!repeat_count : integer ; {how many times to repeat the next row?}
  4133. @!rows_left : integer ; {how many rows left?}
  4134. @!turn_on : boolean ; {are we black here?}
  4135. @!h_bit : integer ; {what is our horizontal position?}
  4136. @!run_ct : integer ; {how many bits of current color left?}
  4137.  
  4138. @ Another necessary section of code skips over any \MF\ \.{special}s between
  4139. characters and before or after the postamble.
  4140.  
  4141. It is convenient to use the following definitions in creating readable |case|
  4142. statements; they will also be necessary for interpreting the \.{DVI} file.
  4143.  
  4144. @d four_cases(#)==#,#+1,#+2,#+3
  4145. @d eight_cases(#)==four_cases(#),four_cases(#+4)
  4146. @d sixteen_cases(#)==eight_cases(#),eight_cases(#+8)
  4147. @d thirty_two_cases(#)==sixteen_cases(#),sixteen_cases(#+16)
  4148. @d sixty_four_cases(#)==thirty_two_cases(#),thirty_two_cases(#+32)
  4149.  
  4150. @p procedure skip_specials ;
  4151. var i, j, k : integer ;
  4152. begin
  4153.    repeat
  4154.       flag_byte := pk_byte ;
  4155.       if flag_byte >= pk_xxx1 then
  4156.          case flag_byte of
  4157.             four_cases(pk_xxx1):
  4158. begin
  4159.    i := 0 ;
  4160.    for j := pk_xxx1 to flag_byte do i := 256 * i + pk_byte ;
  4161.    for j := 1 to i do dummy:=pk_byte ;
  4162. end ;
  4163.             pk_yyy: dummy:=get_32 ;
  4164.             pk_post: do_nothing;
  4165.             pk_no_op: do_nothing;
  4166.             pk_pre, eight_cases(pk_pre+1):
  4167.               abort('Bad pk file; unexpected byte: ', flag_byte:1,'!') ;
  4168. @.Bad pk file@>
  4169.          endcases ;
  4170.    until (flag_byte < pk_xxx1) or (flag_byte = pk_post) ;
  4171. end ;
  4172.  
  4173. @* Pixel file format.
  4174. A \.{PXL} file is an expanded raster description of a single font at a
  4175. particular resolution and contains essentially the same information as
  4176. that contained in a \.{GF} file.  \.{PXL} files are used by many existing
  4177. device-driver programs for dot matrix devices. By convention, \.{PXL} files
  4178. are for 200 pixels per inch. However, the LN03 requires pixel files generated
  4179. at 300 pixels per inch; the font naming conventions for access to these files
  4180. are specified elsewhere.
  4181.  
  4182. All words in a \.{PXL} files are in 32-bit format, with the four lower
  4183. bits zero on 36-bit machines. The raster information is contained in a
  4184. sequence of binary words which record white pixels as zeros and black
  4185. pixels as ones.
  4186.  
  4187. The first word of the \.{PXL} file and the last word contain the |pxl_id|
  4188. which is currently equal to 1001.
  4189.  
  4190. This first word is followed by a sequence of raster information words where
  4191. each line of pixels in the glyphs is represented by one or more words of
  4192. binary information. The number of words used to represent each row of pixels
  4193. for any particular glyph is fixed and it is set by the value of
  4194. |max_m-min_m+1| for that particular glyph. Each white pixel is represented by
  4195. a zero and each black pixel is represented by a one in the corresponding bit
  4196. positions (the first 32 only of each word on 36-bit machines). The unused bit
  4197. positions toward the end of each set of words for each row of pixels are
  4198. filled with zeros. It should be noted that this representation is more
  4199. wasteful of space than it needs to be, but it may possibly simplify the
  4200. subsequent use of the information by a device-driver program.
  4201.  
  4202. The font directory follows, occupying a fixed position with respect to the
  4203. end of the file (in words 517 through 6 from this end), and assigns 4
  4204. words for each of the potential 128 different glyphs that could be
  4205. contained in this particular font in the order of their ascending ascii
  4206. values (not in the order that the glyphs appear in the raster section,
  4207. which may be entirely arbitrary). This means that the first four words are
  4208. for the ascii zero glyph.  All four words reserved for any missing glyphs
  4209. are set to zero.  A detailed description of these directory entries is given in
  4210. the next section.
  4211.  
  4212. The final five words in the \.{PXL} file contain information relative to
  4213. the entire file.
  4214.  
  4215. The first of these five words is a checksum which should
  4216. match the checksum contained in the \.{TFM} file that \TeX\ used in
  4217. reference to this font, although, if this checksum is zero, no validity
  4218. checking will be done.
  4219.  
  4220. The second of these five words is an integer that is 1000 times the
  4221. magnification factor at which this font was produced.
  4222.  
  4223. The third word contains the design size of the font measured in \.{FIXes}
  4224. ($2^{-20}$ unmagnified points).
  4225.  
  4226. The fourth word contains a pointer to the first word of the font directory.
  4227.  
  4228. The fifth (and last word of the entire file) contains a duplicate of the
  4229. |pxl_id| as contained in the first word of the file.
  4230.  
  4231. @d pxl_id=1001 {current version of \.{PXL} format}
  4232.  
  4233. @ As mentioned above, the pixel file's glyph directory consists of four
  4234. longwords for each possible glyph.  The use of each of these longwords is as
  4235. follows:
  4236.  
  4237. \yskip\hang\&{Word 0.} This is treated as being split into two unsigned
  4238. sixteen-bit quantities, containing the Pixel Width in the left half-word (the
  4239. leftmost 16 bits) and the Pixel Height in the right half-word (the next 16
  4240. bits). These dimensions are those of the smallest bounding-box, measured in
  4241. pixels, and they have nothing necessarily to do with the width and height
  4242. figures that appear in the \.{TFM} file.
  4243.  
  4244. \yskip\hang\&{Word 1.} Again is split into two sixteen-bit half-words, but
  4245. this time they are each treated as \\{signed} quantities. Together, these give
  4246. the offset of the glyph's reference point \&{from} its upper-left-hand corner
  4247. of the bounding box, measured in pixels, with the $x$-offset ($\delta x$) in the left
  4248. half-word and the $y$-offset ($\delta y$) in the right half-word. Two's complement
  4249. representation is used.  Remember that although the positive $x$ direction means
  4250. `rightward' and positive $y$ is `downward' on the page, these offsets specify
  4251. the distance \&{to} the reference point \&{from} the upper left-hand corner of
  4252. the raster with +ve $x$ implying that the left-most column of the raster is to
  4253. the \&{left} of the reference point, and +ve $y$ implying that the top-most
  4254. row of the raster is \&{above} the baseline.
  4255.  
  4256. \yskip\hang\&{Word 2.} This contains the number of the word in this \.{PXL}
  4257. file where the Raster Description for this particular glyph begins, measured
  4258. from the first word of the file, which is numbered zero.
  4259.  
  4260. \yskip\hang\&{Word 3.} Contains the \.{TFM} width, measured in \.{FIXes},
  4261. where 1 \.{FIX} is $1/(2^{20})$ times the design size.
  4262.  
  4263. We now define an appropriate structure to hold such directory entries:
  4264.  
  4265. @<Types in the outer block@>=
  4266.      @!signed_word= [word] -32768..32767;@/
  4267.      @!pxl_dir_type=packed record@/
  4268.         num_cols,num_rows : sixteen_bits;
  4269.         x_off,y_off :  signed_word;
  4270.         addr : [byte(4)] integer;
  4271.         tfm_width : [byte(4)] integer
  4272.      end;
  4273.  
  4274. @ We need a |file| object to reference the pixel file, and also an array to hold
  4275. its directory.  Furthermore, when a pixel file directory is read in, we need
  4276. variables |magnification| and |dsize| to hold quantities taken from the last
  4277. five longwords of the file.
  4278.  
  4279. @<Globals in the outer block@>=
  4280.   @!pxl_file: byte_file;
  4281.   @!pxl_dir : packed array [0..127] of pxl_dir_type;
  4282.   @!pxl_block,@!pxl_ptr : integer;
  4283.   @!pxl_size:integer;
  4284.   @!magnification,@!dsize:real;
  4285.  
  4286. @*Procedures to access the pixel file.  The following definitions and
  4287. declarations provide a means of accessing bytes, words (signed \AM\ unsigned)
  4288. and longwords in the |pxl_file|.
  4289.  
  4290. @d pxl_byte(#)==pxl_file^[#]
  4291.  
  4292. @d pxl_word(#)==(pxl_byte(#+1)+256*pxl_byte(#))
  4293.  
  4294. @d pxl_long(#)==(pxl_byte(#+3)+256*(pxl_byte(#+2)+@|256*(pxl_byte(#+1)+@|
  4295. 256*pxl_byte(#))))
  4296.  
  4297. @d must_get(#)==
  4298.   if # = VAX_block_length then
  4299.   begin  get(pxl_file,VAX_continue);
  4300.     incr(pxl_block); #:=0
  4301.   end
  4302.  
  4303. @p  procedure move_to_pxl(n:integer);
  4304.   begin
  4305.     if n div VAX_block_length <> pxl_block then
  4306.     begin pxl_block:=n div VAX_block_length;
  4307.       VAX_find_block(pxl_file,pxl_block+1,VAX_continue)
  4308.     end;
  4309.     pxl_ptr:=n mod VAX_block_length
  4310.   end;
  4311.  
  4312. @ The |long_pxl| function reads a longword (in Bigendian order) from the
  4313. |pxl_file| and converts it to correct (signed) VAX longword notation.
  4314.  
  4315. @p  function long_pxl(var index:integer) : integer;
  4316.   begin
  4317.     if pxl_byte(index)<=@"80 then
  4318.       long_pxl:=pxl_long(index)
  4319.     else
  4320.       long_pxl:=pxl_byte(index+3)+256*(pxl_byte(index+2)+256*(
  4321.                 pxl_byte(index+1)+256*(pxl_byte(index)-256)));
  4322.     index:=index+4;
  4323.     must_get(index)
  4324.   end;
  4325.  
  4326. @ The |sign_pxl_word| function reads a signed shortword (two bytes).
  4327.  
  4328. @p  function sign_pxl_word(var index:integer) : integer;
  4329.   begin
  4330.     if pxl_byte(index)>=@"80 then
  4331.       sign_pxl_word:=pxl_word(index)-@"10000
  4332.     else
  4333.       sign_pxl_word:=pxl_word(index);
  4334.     index:=index+2;
  4335.     must_get(index)
  4336.   end;
  4337.  
  4338. @ The |word_pxl| function reads an unsigned two-byte word.
  4339.  
  4340. @p  function word_pxl(var index:integer) : integer;
  4341.   begin word_pxl:=pxl_word(index);
  4342.     index:=index+2; must_get(index)
  4343.   end;
  4344.  
  4345. @*Handle rasters of oversized glyphs.  Because of a physical limitation of the
  4346. LN03, it is not legal to download the glyph of a character which would occupy
  4347. more than 5,700 bytes.  (Details appear under ``\&{Copy one character's
  4348. rasters}''.)
  4349.  
  4350. Any such glyph which has not been downloaded will have its |loaded| flag set to
  4351. |no| in its |glyph_map| entry.  When we attempt to add such a character to
  4352. the output lists, we add instead a separate segment which indicates that it
  4353. holds a single oversized glyph.  When this segment is eventually processed,
  4354. we output the glyph itself as a bitmap dump, using sixel graphics.
  4355.  
  4356. To save us having to keep on opening and closing the pixel file containing
  4357. the glyph, we maintain the ``name'' of the currently open file in a global
  4358. string, and only open a new file if the glyph required comes from a
  4359. different \TeX\ font. The global variable |open_file_name| is a textual
  4360. representation of the full file specification of the currently open pixel
  4361. file. (Note that this same variable name also appears as a \&{formal
  4362. parameter} of the |add_txf_to_lnf| function; it is referenced in the
  4363. $\langle$Open the file containing the glyph bitmaps$\rangle$ W{\mc EB}
  4364. section, used in the following code, and the aforementioned function.)
  4365.  
  4366. @<Globals...@>=
  4367. @!open_file_name : file_spec;
  4368.  
  4369. @ Of course, when we start (and even after we've created the font file(s) for
  4370. down-loading) there is no pixel file open.  Let's indicate that:
  4371.  
  4372. @<Set initial...@>=
  4373. open_file_name:='';
  4374.  
  4375. @ Procedure |print_glyph| has to refer to |change_pixel_file|, which is more
  4376. convenient to declare along with the font downloading procedures.  So we'll
  4377. cheat and put a |forward| declaration here!
  4378.  
  4379. @p procedure change_pixel_file( TeX_font : integer); forward;
  4380.  
  4381. @ The procedure |print_glyph| opens the associated pixel file (if necessary)
  4382. by calling |change_pixel_file|.  It firstly outputs absolute positioning
  4383. commands to establish the correct ``printing'' position (the strange
  4384. |bitmap_offset| was incorporated when the reference to the glyph was stored
  4385. in the |segment|), and then outputs suitable escape sequences to locate the
  4386. printing position at the correct offset relative to that position, as
  4387. determined by the glyph's first pixel relative to its reference point.
  4388.  
  4389. If the pixels come from a |pixel_file|, then we can locate the required
  4390. character glyph through the character directory.  In the case of a
  4391. |packed_file|, it may happen than this character has already been output, and
  4392. so its position within the file is known; in this case either it was the last
  4393. character read (has already been dumped as a bitmap), when its glyph will be in
  4394. the |lnf_file|: otherwise we position |pk_loc| to the correct position in the
  4395. file.  If the character has never been read (|ch_loc=0|) then we read and
  4396. process characters into the \&{same} part of the |lnf_file| until the desired
  4397. character has been read.  When reading |packed_file|s, we utilize an entry in
  4398. the |pxl_dir| in preference to the globals |x_off|, etc, so save repetition of
  4399. many lines of \.{WEB} code.
  4400.  
  4401. The lines of pixel rasters are then output, six at a time, in sixel graphics
  4402. format.
  4403.  
  4404. @p procedure print_glyph(@!glyph_seg : segment; @!y_loc : integer);
  4405.   var  @!sixel_line : array [1..6,1..300] of eight_bits;
  4406.     @!i,@!j,@!k,@!l,@!m,@!n,@!len : integer;
  4407.     @!last_sixel,@!matching: integer;
  4408.     @!ch : integer;
  4409.   @<Declare the procedure |put_sixel|@> ;
  4410. begin
  4411.   write_ln(ln3_file);
  4412.   change_pixel_file(glyph_seg.font);@/
  4413.   write(ln3_file,csi,y_loc:1,'d',csi,glyph_seg.xs:1,'`');
  4414.   ch:=glyph_seg.ch;
  4415.   with pxl_dir[ch] do
  4416.   begin
  4417.     @<Read raster for oversized glyph into memory@> ;
  4418.     @<Prepare to write glyph bitmap at correct position@> ;
  4419.     write_ln(ln3_file,dcs,'9;0;1q');    {Prepare to meet a sixel dump!}
  4420.     @<Convert glyph's raster to sixels@>;
  4421.     write_ln(ln3_file,st);              {Finish sixel dump}
  4422.   end; {with}
  4423. end;
  4424.  
  4425. @ We are making use of a record of |pxl_dir| to hold the various quantities
  4426. regarding the dimensions of the glyph which have been (or are about to be) read
  4427. from the font file.  In the case of an unpacked raster, we need only position
  4428. the file to the correct longword, but for a packed file, it's now time to
  4429. start reading the bytes and reconstituting the raster in a free part of the
  4430. |lnf_file|.
  4431.  
  4432. @<Read raster for oversized glyph into memory@>=
  4433. if pxl_ident=pixel_file then move_to_pxl(addr*4)
  4434. else begin
  4435.   ras_beg:=2*VAX_block_length;
  4436.   if ch_loc[ch]=0 then
  4437.     @<Find packed raster and read into memory@> ;
  4438.   if char_num<>ch then
  4439.     @<Move back in packed file and read raster into memory@> ;
  4440.   base:=ras_beg
  4441. end;
  4442.  
  4443. @ If the character has not yet been read from the packed font file, we need to
  4444. read forwards until the character is met.  We then are able to access its
  4445. raster, which will have been created in memory as the character was read.
  4446. The character cannot be missing or the font download would not have resulted
  4447. in the |loaded| field having the value |no|.
  4448.  
  4449. @<Find packed raster and read into memory@>=
  4450. repeat ras_len:=0;@/
  4451.    @<Unpack and store character@>;
  4452.    skip_specials
  4453. until (ch_loc[ch]<>0) or (flag_byte=pk_post);
  4454.  
  4455. @ If we've previously read the wanted character's glyph, but it has since been
  4456. superseded by another's, we need to backtrack the packed font file to the
  4457. correct position and read the raster all over again.
  4458.  
  4459. @<Move back in packed file and read raster into memory@>=
  4460. begin {character not already in |lnf_file|}
  4461.    pk_loc:=ch_loc[ch];
  4462.    VAX_find_block(pxl_file,(pk_loc div VAX_block_length)+1,VAX_continue);
  4463.    ras_len:=0; flag_byte:=pk_byte; {We know there's no \.{special}s}@/
  4464.    @<Unpack and store character@>;
  4465.    skip_specials
  4466. end
  4467.  
  4468. @ Having read the glyph into memory, we now need to position the LN03 to start
  4469. writing its first sixel at the appropriate position relative to the reference
  4470. point.  We do this by sending Horizontal and Vertical Positioning control
  4471. sequences, either Relative for forward movement, or Backward when negative
  4472. movement is necessary.  The signs are reversed because the offsets are those
  4473. of the top left-most pixel of the glyph relative to the reference point.
  4474.  
  4475. @<Prepare to write glyph bitmap at correct position@>=
  4476. if x_off<0 then
  4477.   write(ln3_file,csi,abs(x_off):1,'a')  {Horizontal Position Relative}
  4478. else
  4479.   if x_off>0 then write(ln3_file,csi,x_off:1,'j');{Horizontal Position Backward}
  4480. if y_off<0 then
  4481.   write(ln3_file,csi,abs(y_off):1,'e')  {Vertical Position Relative}
  4482. else
  4483.   if y_off>0 then write(ln3_file,csi,y_off:1,'k') {Vertical Position Backward}
  4484.  
  4485. @ This routine outputs the sixel character, |x|, in the optimum sixel encoding
  4486. making use of repeat counts if the same sixel is repeated more than twice.
  4487. The current output record is terminated if it's getting too long, and a new
  4488. one commenced.
  4489.  
  4490. @<Declare the procedure |put_sixel|@>=
  4491. procedure @!put_sixel(x:integer);
  4492. begin
  4493.   if x = last_sixel then incr(matching) else
  4494.     @<Output |matching| copies of |last_sixel|@> ;
  4495.   if len >=128 then
  4496.   begin
  4497.     write_ln(ln3_file);
  4498.     len:=0
  4499.   end
  4500. end
  4501.  
  4502. @ If we have a new sixel (|x|) which differs from the previous one
  4503. (|last_sixel|) we have to output the latter the correct number of times.  The
  4504. number of successive calls of |put_sixel| will have been counted in
  4505. |matching|.  After performing the output, we record that |x| has now been
  4506. ``output'' once.
  4507.  
  4508. @<Output |matching| copies of |last_sixel|@>=
  4509. begin
  4510.   if matching < 3 then
  4511.     @<Output explicit copies of |last_sixel|@>
  4512.   else
  4513.     @<Output repeat count for |last_sixel|@> ;
  4514.   matching:=1;
  4515.   last_sixel:=x
  4516. end
  4517.  
  4518. @ If |last_sixel| has only been output once or twice, we send the explicit
  4519. copies of it
  4520.  
  4521. @<Output explicit copies of |last_sixel|@>=
  4522. begin
  4523.   len:=len+matching;
  4524.   while matching > 0 do
  4525.   begin
  4526.     write(ln3_file,chr(last_sixel));
  4527.     decr(matching)
  4528.   end
  4529. end
  4530.  
  4531. @ On the other hand, it's more efficient to use a sixel repeat count if there
  4532. are three or more copies of |last_sixel| required.  The introducer for such a
  4533. repeat count is the `\.!' character, which is followed by the ASCII character
  4534. representation of the count.  The actual sixel to be output then follows this
  4535. count.
  4536.  
  4537. @<Output repeat count for |last_sixel|@>=
  4538. begin
  4539.   write(ln3_file,'!',matching:1,chr(last_sixel));
  4540.   len:=len+5
  4541. end
  4542.  
  4543. @ To produce the bitmap sixel dump of the glyph, we read the pixels into the
  4544. array |sixel_line|; this contains 6 rows, each capable of holding 300
  4545. |eight_bits|, containing 8 pixels of the character's raster.  We index this with
  4546. |k|, which thus runs |1..6|
  4547.  
  4548. Variable |i| is used to run through the |num_rows| lines of rasters, whilst |j|
  4549. indexes the bytes from the pixel file, and into |sixel_line|.
  4550.  
  4551. @<Convert glyph's raster to sixels@>=
  4552. i:=0; k:=1;
  4553. while i < num_rows do
  4554. begin
  4555.   @<Read rows of pixels into sixel buffer@>;
  4556.   incr(k); incr(i);
  4557.   if k > 6 then
  4558.   begin
  4559.     @<Output line of sixels@>;
  4560.     k:=1
  4561.   end
  4562. end;
  4563. if k>1 then
  4564. begin
  4565.   while k <= 6 do
  4566.   begin   {Fill in bottom incomplete rows of sixels}
  4567.     for j:=1 to (num_cols+7) div 8 do
  4568.       sixel_line[k,j]:=0;
  4569.     incr(k)
  4570.   end;
  4571.   @<Output line of sixels@>
  4572. end
  4573.  
  4574. @ The following section outputs the six rows of pixels in
  4575. |sixel_line| as $8\times{|num_cols|}$ columns of sixel graphics.
  4576.  
  4577. The variable |m| runs through the values 128,64,32,..,1, and is used to divide
  4578. an |eight_bit| to extract the most-significant bit of eight pixels in rows 6..1
  4579. (indexed by |n|) of |sixel_line|.  Each bit is transferred into the least
  4580. significant bit of |l|, initially zeroed for each sixel.
  4581.  
  4582. As each sixel is completed, it is output to the \.{LN3} file.  To prevent the
  4583. output of too many characters to the |ln3_file|, which is of type |text|, the
  4584. output is broken every 128 bytes.  A ``graphics'' end-of-record is inserted
  4585. after every line of sixels.
  4586.  
  4587. @<Output line of sixels@>=
  4588. len:=0; last_sixel:=0; matching:=0;
  4589. for j:=1 to (num_cols+7) div 8 do
  4590. begin
  4591.   m:=128;
  4592.   while m>0 do
  4593.   begin
  4594.     l:=0;
  4595.     for n:=6 downto 1 do
  4596.       if odd(sixel_line[n,j] div m) then
  4597.         l:=2*l + 1
  4598.       else
  4599.         l:=2*l;
  4600.     put_sixel(l+63);
  4601.     m:=m div 2
  4602.   end;
  4603. end;
  4604. put_sixel(0);   {Flushes the |last_sixel| from the ``buffer''}
  4605. write_ln(ln3_file,'-')  {Sixel `CR-LF'}
  4606.  
  4607.  
  4608. @ We are now in a position to be able to include the declaration of |ord_text|
  4609. into the \PASCAL\ generated from the \.{WEB}
  4610.  
  4611. @p @<Declare the |ord_text| Procedure@>;
  4612. @*Output the individual segments forming a page.  At the end of each page, we
  4613. have a number of linked lists containing each of the segments set on any
  4614. individual ``line'' of the paper.  We have to re-order these, such that they
  4615. are output in the most efficient manner, with appropriate changes of (lnf)
  4616. font as necessary.
  4617.  
  4618. When pages are actually being ``set'', |font_in_use[lnf]| will contain either
  4619. -1, which indicates that the LN03 font required is not currently mapped to a
  4620. |SGR| (Select Graphic Rendition) code, or a number in the range |0..9|, from
  4621. which the |SGR| font number to be used to invoke that font as the current one is
  4622. derived (by adding 10).
  4623.  
  4624. The array |who_uses| maps these |SGR| indices back to the number of an LN03
  4625. font.  Finally, the current |SGR| in use is held in |cur_out|, to minimise
  4626. the number of |SGR| commands that need to be included in the output stream.
  4627.  
  4628. @<Globals...@>=
  4629. @!font_in_use:array [0..max_lnfonts] of -1..max_SGR;
  4630. @!who_uses:array [0..max_SGR] of -1..max_lnfonts;
  4631. @!cur_out:0..max_SGR;
  4632.  
  4633. @ First of all, we shall require a procedure to output an arbitrary string
  4634. (most commonly an escape sequence) to the LN03 printer.  This will typically
  4635. be used to activate the |HPA| (Horizontal Position Absolute) control sequence.
  4636.  
  4637. @p  procedure esc_out(str:varying[str_len] of char);    {Escape sequence to LN3}
  4638.   begin
  4639.     ln3_cnt:=ln3_cnt+VAX_length(str);
  4640.     write(ln3_file,str)
  4641.   end;
  4642.  
  4643. @ Then we require a procedure to output as much of a segment as
  4644. possible to the current line, and delete that section from the segment as
  4645. stored before returning.
  4646.  
  4647. One useful extension of VAX-\PASCAL\ that we utilize are the functions |min|
  4648. and |max|.  These each take an arbitrary number of parameters, and return a
  4649. result, of the same type, that is equal to the smallest or largest
  4650. (numerically) of the parameters provided.
  4651.  
  4652. @d VAX_min==@= min@>
  4653. @d VAX_max==@= max@>
  4654.  
  4655. @p  procedure print_seg;
  4656.   var @!text_len,@!uselen,@!i,@!j,@!ln_font,@!delta_x:integer;
  4657.     @!csi_present:integer;
  4658.   begin
  4659.     with cur_seg^ do
  4660.     begin
  4661.       text_len:=VAX_length(text);
  4662.       uselen:=VAX_min(text_len,max_line-ln3_cnt,max_print-ln3_print);
  4663.       if (uselen>0) or (text_len=0) then        {`Use' empty segment}
  4664.       begin     {Otherwise, throw segment back if none of it will fit!}
  4665.         if uselen <> text_len then              {Text won't all fit!}
  4666.           @<Determine position at which to split output line@>;
  4667.         @<Select correct font, if necessary@> ;
  4668.         @<Set horizontal position on paper@>;
  4669.         write(ln3_file,substr(text,1,uselen));
  4670.         @<Count characters output to printer, perform split if necessary@>
  4671.       end
  4672.     end
  4673.   end;
  4674.  
  4675. @ If a segment is too long to fit in its entirety upon the output line to be
  4676. sent to the LN03, we split it into smaller segments.  This cannot be done at
  4677. an arbitrary cut-off point, since the segment might include embedded control
  4678. sequences (which will be |HPR| ones, ending with `\.a'); therefore, we need to
  4679. select a split point which will not destroy the contiguity of such sequences.
  4680.  
  4681. @<Determine position at which to split output line@>=
  4682. begin
  4683.     csi_present:=0;
  4684.     @<Find rightmost \.{ESC} or \.{CSI} character in segment@> ;
  4685.     @<Split \\{after} the |HPR| control sequence, if in last 8 characters@>
  4686. end
  4687.  
  4688. @ Finding the right-most control sequence starts off by locating the character
  4689. which introduces it;  this used to be \.{ESC} but nowadays the eight-bit
  4690. \.{CSI} character is used instead.
  4691.  
  4692. @<Find rightmost \.{ESC} or \.{CSI} character in segment@>=
  4693. for i:=1 to uselen do
  4694.     if (text[i] = csi) or (text[i] = esc) then csi_present:=i
  4695.  
  4696. @ If that right-most control sequence introducer was earlier than eight
  4697. characters from the end of the part of the string that we \\{can} use (or
  4698. didn't exist) there's no need to split the segment.  Otherwise, we need to
  4699. locate the `\.a' which completes the Horizontal Position Relative sequence,
  4700. and split after it.
  4701.  
  4702. @<Split \\{after} the |HPR| control sequence, if in last 8 characters@>=
  4703. if (csi_present>0) and ((uselen-csi_present)<=8) then
  4704. begin i:=csi_present;
  4705.   while (i<uselen) and (text[i]<>'a') do incr(i);
  4706.   if text[i]<>'a' then uselen:=csi_present-1
  4707. end
  4708.  
  4709. @ Whenever we output a segment, we must ensure that the LN03 will image the
  4710. characters therein from the correct downloaded font.  The following code
  4711. firstly checks whether the font is currently designated (and thus selectable)
  4712. within the LN03.  It then selects the necessary font by outputting a suitable
  4713. |SGR| (Select Graphic Rendition) to access the appropriately designated font.
  4714.  
  4715. @<Select correct font, if necessary@>=
  4716. if font_in_use[font]<0 then     {not currently designated}
  4717. @<Select Graphic Rendition@>;
  4718. if font_in_use[font]<>cur_out then
  4719. begin cur_out:=font_in_use[font];@/
  4720.   esc_out(csi+str_int(10+cur_out)+'m'); {|SGR| for the font}
  4721. end
  4722.  
  4723. @ Although we permit a maximum of |max_lnfonts| to have been downloaded to the
  4724. LN03, the machine itself may only access one of ten of these through the
  4725. ISO-standard |SGR| (Select Graphic Rendition) control sequence.  If a
  4726. different font is required, it may be necessary to designate that it shall be
  4727. selectable by a different |SGR| sequence.
  4728.  
  4729. @<Select Graphic Rendition@>=
  4730. begin
  4731.     font_in_use[who_uses[max_SGR]]:=-1;
  4732.     font_in_use[font]:=max_SGR;
  4733.     who_uses[max_SGR]:=font;
  4734.     esc_out(dcs+'1;'+str_int(10+max_SGR)+'}U0000'+@=dec@>(font,2,2)+
  4735.                     '002SK00GG'+st)
  4736. end
  4737.  
  4738.  
  4739. @ We need to output appropriate ISO-standard control sequences to position the
  4740. segment at the correct position on the current line; if the segment is the
  4741. first to be output on the current line, this positioning should use the |HPA|
  4742. (Horizontal Position Absolute) sequence, otherwise the |HPR| (Horizontal
  4743. Position Relative) sequence is preferred.
  4744.  
  4745. @<Set horizontal position on paper@>=
  4746. if cur_seg=out_list then                {Leftmost segment of line}
  4747.     esc_out(csi+str_int(xs)+'`')        { HPA to |xs|}
  4748. else@/
  4749. begin
  4750.     delta_x:=xs-prev_seg^.xe;   {Distance from end of next leftward segment}
  4751.     if delta_x>0 then esc_out(csi+str_int(delta_x)+'a') {HPR}
  4752.     else if delta_x<0 then abort('Internal error (-ve delta x)')
  4753. @:fatal error Internal error ...}{\quad\.{Internal error}@>
  4754. @:fatal error Internal error negative delta x}{\qquad\.{(-ve delta x)}@>
  4755. end
  4756.  
  4757. @ After we have output the first |uselen| characters to the \.{LN3} file, we
  4758. then check whether that's finished off this segment.  If it has, we can
  4759. discard the segment, but otherwise we perform the split at the predetermined
  4760. point.
  4761.  
  4762. @<Count characters output to printer, perform split if necessary@>=
  4763. ln3_cnt:=ln3_cnt+uselen; ln3_print:=ln3_print+uselen;
  4764. if uselen=text_len then
  4765.   cur_seg:=nil                  {All of segment used}
  4766. else
  4767.   @<Split segment which would create too long an output line@>
  4768.  
  4769. @ Having previously determined the split point for the segment (including none
  4770. at all), we now reduce the segment to the part following that point.
  4771.  
  4772. This is complicated by the fact that we must determine whereabouts we have
  4773. actually got to on the current line.  To do this, we increment the ``current
  4774. position'' by each character's width, whilst any |HPR| sequences have to be
  4775. ``read'' in as numbers and added to the count.
  4776.  
  4777. @<Split segment which would create too long an output line@>=
  4778. begin i:=1;
  4779.   while i<=uselen do
  4780.   begin
  4781.     if (text[i]<>esc) and (text[i]<>csi) then
  4782.       xs:=xs+ch_widths[font,ord(text[i])]
  4783.     else
  4784.       @<Read back the horizontal skip specified in the |HPR| sequence@> ;
  4785.     incr(i);
  4786.   end;
  4787.   text:=substr(text,uselen+1,text_len-uselen)
  4788. end
  4789.  
  4790. @ Whenever we meet a \.{ESC} or \.{CSI} character, we assume that it starts a
  4791. Horizontal Position Relative command, locate the first digit of its parameter
  4792. (which will follow a `\.[' after the \.{ESC} or come immediately after the
  4793. \.{CSI}), and the `\.a' which finishes the control sequence.  The characters in
  4794. between represent a decimal number, which we can ``read'' using a Vax-\PASCAL\
  4795. extension.  The current placing position is then incremented by this amount.
  4796.  
  4797. @<Read back the horizontal skip specified in the |HPR| sequence@>=
  4798. begin
  4799.   if text[i]=esc then j:=i+2 else j:=i+1;
  4800.   while text[j]<>'a' do incr(j);
  4801.   if text[i]=esc then
  4802.     VAX_string_read(substr(text,i+2,j-i-2)+' ',delta_x)
  4803.   else
  4804.     VAX_string_read(substr(text,i+1,j-i-1)+' ',delta_x);
  4805.   xs:=xs+delta_x; i:=j;
  4806. end
  4807.  
  4808.  
  4809.  
  4810. @ As we shall see later, if the string of characters in a \TeX\
  4811. \.{\\special} command commences with the character string |'SX '| (i.e.,
  4812. `old style'), or |'ln03:plotfi[le] '| or |'ln03:sixel '| (`new style'), a
  4813. segment will be inserted into the page list referencing the appropriate
  4814. file; when the time comes to process that segment, the following procedure
  4815. will be invoked.  The |text| field of |cur_seg| will be the name of a
  4816. VAX/VMS file which is assumed to contain an image encoded into DEC's
  4817. standard ``sixel'' format.
  4818.  
  4819. This file is then opened, and the records therein copied to the \.{.LN3}
  4820. file; the |y_position| parameter, and the starting position recorded in
  4821. field |xs| are used in appropriate escape sequences to position this
  4822. graphics dump at the desired position on the page before this operation.
  4823.  
  4824. The name of the file included is reported on the terminal and in the log file.
  4825. @p procedure Copy_sixel_file(@!y_position:integer);
  4826. var
  4827.   @!open_stat : integer; {records whether |open| was successful}
  4828.   @!sixel_record, {holds records being copied from included file}
  4829.   @!stat_msg : varying [256] of char; {just holds an error indication}
  4830.   @!sixel_file : text;  {The file of graphics details to be copied}
  4831. begin
  4832.   open(sixel_file,cur_seg^.text,@=readonly@>,@=256@>,
  4833.          @=user_action@>:=file_open,VAX_continue);
  4834.   open_stat := status(sixel_file);
  4835.   if open_stat<>0 then
  4836.   begin
  4837.     case open_stat of
  4838.       2: stat_msg := 'PAS-E-ERRDUROPE';
  4839.       3: stat_msg := 'PAS-E-FILNOTFOU';
  4840.       5: stat_msg := 'PAS-E-ACCMETINC';
  4841.       6: stat_msg := 'PAS-E-RECLENINC';
  4842.       7: stat_msg := 'PAS-E-RECTYPINC';
  4843.       8: stat_msg := 'PAS-E-ORDSPEINC';
  4844.     othercases
  4845.        stat_msg := str_int(open_stat);
  4846.     endcases;
  4847.   error('Couldn''t open \special inclusion: ',cur_seg^.text,
  4848.          ' - ignored (status=',stat_msg,')');
  4849. @:Error: Couldn't open special inclusion}{\quad\.{Couldn't open \\special inclusion}@>
  4850.   end else
  4851.   begin
  4852.     monitor(' ('+def_file_name);
  4853.     print_ln('Sixel graphics included from: ',def_file_name);
  4854. @.Sixel graphics included...@>
  4855.     reset(sixel_file);
  4856.     write(ln3_file,csi,y_position:1,'d',csi,cur_seg^.xs:1,'`');
  4857.     while not eof(sixel_file) do
  4858.     begin
  4859.       read_ln(sixel_file,sixel_record);
  4860.       write_ln(ln3_file,sixel_record)
  4861.     end;
  4862.     close(sixel_file,@=disposition:=save@>,VAX_continue);
  4863.     monitor(') ');
  4864.     write_ln(ln3_file,SSU_pixel);     {Make sure that we still have pixel SSUs}
  4865.   end;
  4866. end;
  4867.  
  4868. @ Whilst traversing the lists, we require some additional variables:
  4869. During this output phase, we split the lines
  4870. of output such that no line contains more than |max_line| characters
  4871. altogether, of which no more than |max_print| may be printable ones.  The
  4872. counters |ln3_cnt| and |ln3_print| permit us to exercise this control.  The
  4873. |boolean| variable |top_of_page| controls the inclusion of a |FF| character in
  4874. the output file to split the pages.
  4875.  
  4876. If the page contains glyphs in both of the possible orientations, this is
  4877. recorded in |two_orientations|.  Segments corresponding to the orientation
  4878. not currently being output are shuffled off onto the |deferred_list| whilst
  4879. this processing takes place.
  4880.  
  4881. @<Globals in the ...@>=
  4882.   @!out_list,@!prev_seg,@!right_seg,@!left_seg:^segment;
  4883.   @!x_left,@!x_max:integer;
  4884.   @!ln3_cnt,@!ln3_print:integer;
  4885.   @!still_fits,top_of_page:boolean;
  4886.   @!two_orientations:boolean;
  4887.   @!deferred_list:^segment;
  4888.  
  4889.  
  4890. @ The actual traversing of the lists takes place here; since we now support
  4891. the concept of mixing orientations on the one page, it's sometimes necessary
  4892. to traverse the lists twice!
  4893.  
  4894. @<Output contents of page@>=
  4895. repeat
  4896.   two_orientations := false;  {assume that we \&{don't} need two passes!}
  4897.   for k:=y_top to y_bot do    {Cycle through each possible list}
  4898.     if seg_list[k] <> nil then
  4899.     begin
  4900.       out_list:=nil; deferred_list:=nil;
  4901.       repeat                    {until we've emptied current line}
  4902.         @<Save segments which require opposite orientation@> ;
  4903.         @<Process bitmaps and other ``empty'' segments@>;
  4904.         @<Construct a list of non-overlapping segments@> ;
  4905.         @<Output list of non-overlapping segments@> ;
  4906.       until seg_list[k]=nil;
  4907.       if deferred_list<>nil then two_orientations:=true;
  4908.       seg_list[k]:=deferred_list
  4909.     end;
  4910.     if two_orientations then {prepare for second (and last) pass}
  4911.     begin
  4912.       if cur_orient=landscape then new_orient:=portrait
  4913.       else new_orient:=landscape;
  4914.       @<Change page orientation@>
  4915.     end;
  4916. until not two_orientations;
  4917. if lost_seg <> nil then
  4918. begin cur_seg:=lost_seg;
  4919.   finish_seg            {Output remaining errors}
  4920. end;
  4921.  
  4922. @ If, when outputting stored information in |cur_orient|, we encounter some
  4923. segment(s) in the opposite orientation, we save them in a subsidiary list,
  4924. and stuff them back into |seg_list[k]| after processing the current row.
  4925.  
  4926. @<Save segments which require opposite orientation@>=
  4927. prev_seg:=nil; cur_seg:=seg_list[k];
  4928. repeat
  4929.   right_seg:=cur_seg^.next; {we know that |cur_seg<>nil|}
  4930.   if cur_seg^.direction<>cur_orient then
  4931.   begin
  4932.     if prev_seg<>nil then
  4933.       prev_seg^.next:=right_seg   {in middle of list}
  4934.     else
  4935.       seg_list[k]:=right_seg;     {at beginning of list}
  4936.     cur_seg^.next:=deferred_list; {add segment to front of deferred list}
  4937.     deferred_list:=cur_seg
  4938.   end else
  4939.     prev_seg:=cur_seg;            {latest non-empty node}
  4940.   cur_seg:=right_seg              {next node on |seg_list|}
  4941. until cur_seg=nil
  4942.  
  4943.  
  4944. @ Now that the program can support landscape and portrait orientation within
  4945. the same document, we need a method of telling the LN03 to switch between
  4946. the modes.  This code is called before output of the first object to the
  4947. page, if |new_orient<>orientation|, and again at the end of the page, to
  4948. reset to the default |orientation| for the next page.
  4949.  
  4950. @<Change page orientation@>=
  4951. if new_orient<>cur_orient then
  4952. begin
  4953.   if new_orient=landscape then
  4954.   begin
  4955.     write_ln(ln3_file,csi,PFS_landscape);
  4956.     page_len:=paper_wid+page_x_min; page_wid:=paper_ht+y_land_min;
  4957.     y_min:=page_x_min; x_min:=y_land_min
  4958.   end else
  4959.   begin
  4960.     write_ln(ln3_file,csi,PFS_portrait);
  4961.     page_len:=paper_ht+y_port_min; page_wid:=paper_wid+page_x_min;
  4962.     y_min:=y_port_min; x_min:=page_x_min
  4963.   end;
  4964.   cur_orient:=new_orient
  4965. end
  4966.  
  4967. @ When handling characters from a virtual font, the situation can arise in
  4968. which a totally empty segment (one that contains no text) has been added to
  4969. the list.  Such segments can be discarded!  Other segments can appear to
  4970. contain no text, but actually reference a single oversized glyph, which must
  4971. therefore be output as a bitmap.
  4972.  
  4973. @<Process bitmaps and other ``empty'' segments@>=
  4974. prev_seg:=nil; cur_seg:=seg_list[k];
  4975. if cur_seg<>nil then
  4976. repeat
  4977.   right_seg:=cur_seg^.next; {we know that |cur_seg<>nil|}
  4978.   if (VAX_length(cur_seg^.text)=0) or (cur_seg^.seg_type<>glyph_string) then
  4979.   begin
  4980.     @<Output segment which references an external bitmap@>;
  4981.     if prev_seg<>nil then
  4982.       prev_seg^.next:=right_seg   {in middle of list}
  4983.     else
  4984.       seg_list[k]:=right_seg;     {at beginning of list}
  4985.     cur_seg^.next:=free_seg_list; {add segment to front of free list}
  4986.     free_seg_list:=cur_seg
  4987.   end else
  4988.     prev_seg:=cur_seg;            {latest non-empty node}
  4989.   cur_seg:=right_seg              {next node on |seg_list|}
  4990. until cur_seg=nil
  4991.  
  4992. @ Character glyphs which were too large to be downloaded into the LN03's
  4993. internal fonts have the |seg_type| field of the segment set to |bitmap|;
  4994. when we meet such a segment, we output the bitmap immediately.  Similarly,
  4995. sixel dumps referenced in \.{\\special} commands have the value |sixel_dump|
  4996. for this field, and |Copy_sixel_file| is called to copy the referenced file.
  4997.  
  4998. @<Output segment which references an external bitmap@>=
  4999. case cur_seg^.seg_type of
  5000.   bitmap: print_glyph(cur_seg^,k);
  5001.   sixel_dump: Copy_sixel_file(k);
  5002.   othercases do_nothing;
  5003. endcases
  5004.  
  5005. @ For as many as possible of all the segments which belong at the current
  5006. absolute vertical position, we construct a list in which none of the segments
  5007. overlap each other.  We are then able to output these in one sequence which
  5008. will contain only characters and \&{forward} horizontal movement commands.
  5009.  
  5010. @<Construct a list of non-overlapping segments@>=
  5011. x_left:=1000000000;     {A very high value; ensure |right_seg| \AM\ |left_seg| get set}
  5012. repeat
  5013.   prev_seg:=nil; right_seg:=nil;
  5014.   x_max:=-1;
  5015.   cur_seg:=seg_list[k];
  5016.   @<Find segment of current line which extends furthest to the right@> ;
  5017.   @<Transfer right-most segment to the |out_list|@>
  5018. until right_seg = nil;
  5019.  
  5020. @ We start by locating the segment (of those available) which extends furthest
  5021. to the right, and also the one which precedes it.  These are then pointed to
  5022. by |right_seg| and |prev_seg|, respectively.
  5023.  
  5024. @<Find segment of current line which extends furthest to the right@>=
  5025. while cur_seg<>nil do
  5026. begin
  5027.   if (cur_seg^.xe > x_max) and (cur_seg^.xe <= x_left) then
  5028.   begin x_max:=cur_seg^.xe;
  5029.     right_seg:=cur_seg;         {Points to rightmost segment}
  5030.     left_seg:=prev_seg  {Points to first of at least 2, or is |nil|}
  5031.   end;
  5032.   prev_seg:=cur_seg;
  5033.   cur_seg:=cur_seg^.next;
  5034. end
  5035.  
  5036. @ At this point, we have |right_seg| pointing to the segment which extends
  5037. furthest to the right, whilst |left_seg| will point to the segment immediately
  5038. before this (which may, or may not, overlap |right_seg|).
  5039.  
  5040. We then put it at the front of the |out_list|; further non-overlapping
  5041. segments will later be placed in front of it.  We ensure that the next
  5042. |right_seg| that we select will not overlap by noting the left-most extent of
  5043. the segment we've just put on the |out_list|.
  5044.  
  5045. @<Transfer right-most segment to the |out_list|@>=
  5046. if right_seg <> nil then
  5047. begin
  5048.   @<Remove |right_seg| from list@> ;
  5049.   right_seg^.next:=out_list;            {Put rightmost at start of |out_list|}
  5050.   out_list:=right_seg;
  5051.   x_left:=right_seg^.xs         {Don't consider anything right of its LHS}
  5052. end
  5053.  
  5054. @  If |left_seg| is |nil| then |right_seg| is the first segment associated
  5055. with the line, and so we remove it from the front of the list.  Otherwise, we
  5056. detach it from the middle of the list.
  5057.  
  5058. @<Remove |right_seg| from list@>=
  5059. if left_seg = nil then
  5060.   seg_list[k]:=seg_list[k]^.next
  5061. else
  5062.   left_seg^.next:=left_seg^.next^.next
  5063.  
  5064. @ Once we've constructed the list out of as many segments as possible for the
  5065. current ``line'', we can output them all and go back for some more.
  5066.  
  5067. @<Output list of non-overlapping segments@>=
  5068. while out_list <> nil do
  5069. begin
  5070.   ln3_cnt:=0; ln3_print:=0;     {Nothing as yet on line}
  5071.   esc_out(csi+str_int(k)+'d');  {Move to correct line on paper}
  5072.   cur_seg:=out_list; prev_seg:=nil;
  5073.   still_fits:=true;
  5074.   @<Output current segment, in chunks if necessary@> ;
  5075.   @<Return processed segments to the |free_seg_list|@>;
  5076.   write_ln(ln3_file)
  5077. end
  5078.  
  5079. @ Once we have a single segment ready for output, we call |print_seg| to
  5080. output as much of it as will fit in the space remaining in the output buffer.
  5081. If this is the whole segment, we can then repeat the process with the next
  5082. segment in the |out_list|, noting the address of this segment so that it may
  5083. be returned to the |free_seg_list|.  On the other hand, if |print_seg| decided
  5084. to split the segment, we break out of the loop.
  5085.  
  5086. @<Output current segment, in chunks if necessary@>=
  5087. while (cur_seg <> nil) and still_fits do
  5088. begin right_seg:=cur_seg;
  5089.   print_seg;            {Will leave |cur_seg=nil| if all text output}
  5090.   if cur_seg = nil then
  5091.   begin
  5092.     cur_seg:=right_seg^.next;   {Next segment for output}
  5093.     prev_seg:=right_seg
  5094.   end else
  5095.     still_fits:=false;
  5096. end;
  5097.  
  5098. @ Once a segment (or group of segments) have been output, it is safe to
  5099. discard them.  We do this by returning them to the |free_seg_list|, so that
  5100. new nodes will be allocated therefrom in preference to using |new|.  The first
  5101. such freed segment is that at the begining of the |out_list|, whilst the last
  5102. is that most recently output, noted in |prev_seg|.
  5103.  
  5104. @<Return processed segments to the |free_seg_list|@>=
  5105. if prev_seg <> nil then
  5106. begin prev_seg^.next:=free_seg_list;
  5107.   free_seg_list:=out_list;
  5108.   out_list:=cur_seg
  5109. end
  5110.  
  5111. @ When \.{DVItoLN03} first starts, any output produced will be printed on the
  5112. first page.  Subsequently, it is necessary to insert a form-feed character to
  5113. terminate the preceding page before any output for another page may commence.
  5114. The |boolean| |top_of_page| is used to keep track of whether the form-feed is
  5115. required.
  5116.  
  5117. This code is called whenever something is about to be output to the page: if
  5118. the orientation is incorrect at that time, the appropriate change is made.
  5119. It's also the appropriate time to change paper trays, and select the
  5120. printing mode.
  5121.  
  5122. @<Move to top of form@>=
  5123. if top_of_page then
  5124. begin
  5125.   top_of_page:=false;
  5126.   if pages_printed>0 then write_ln(ln3_file,FF);
  5127.   @<Change tray for second sheet, if requested@>;
  5128.   @<Skip to a \\{recto} page, if necessary@>
  5129. end;
  5130. @<Change page orientation@>
  5131.  
  5132. @ With the DEClaser~2200, it is possible (through the \.{FIRST} and \.{REST}
  5133. selectors of the \.{/FEED\_TRAY} qualifier) to arrange for the second and
  5134. subsequent sheets to be printed on paper from a different input tray than
  5135. that used for the first sheet.  The code in this module effects this tray
  5136. change, when the second sheet to be printed is first met: it  firstly
  5137. determines whether we're printing a \\{recto} page, when duplexing (this
  5138. information is later used for selecting the printing mode for the current
  5139. page).  The ``cascade'' of |if| clauses is to reduce the processing overhead
  5140. except for the first three pages printed.
  5141.  
  5142. The macro here is used to change trays; the control sequence ending in
  5143. |'!v'| is the |DECASTC| (Automatic Sheet-feeder Tray Control) command, to
  5144. select the desired tray.
  5145.  
  5146. @d select_tray(#)==case # of
  5147.   default_tray: do_nothing;
  5148.   othercases
  5149.     write(ln3_file,csi,#:1,'!v')
  5150. endcases
  5151.  
  5152. @d odd_page==(odd(count[0])or(count[0]=0))
  5153.  
  5154. @d recto_page==(duplex_by_page_numbers and odd_page) or
  5155.   (not duplex_by_page_numbers and not odd(pages_printed))
  5156.  
  5157. @d starting_second_sheet==
  5158.   (((print_mode<true_duplex_normal)or(print_mode>true_duplex_tumbled)) and
  5159.    (pages_printed=1)) {non-duplex; change at second page}
  5160.   or
  5161.   (((print_mode=true_duplex_normal)or(print_mode=true_duplex_tumbled)) and
  5162.    this_page_recto) {duplex; change at second sheet}
  5163.  
  5164. @<Change tray for second sheet, if requested@>=
  5165. this_page_recto:=recto_page;
  5166. if pages_printed<=2 then
  5167.   if pages_printed>0 then
  5168.     if starting_second_sheet then
  5169.       if following_tray<>first_tray then
  5170.         select_tray(following_tray)
  5171.  
  5172. @ When we are printing in duplex mode (even simulated duplex, in response to
  5173. the \.{MASTER} option of the \.{/PRINT\_MODE} qualifier), we have to arrange
  5174. to leave a page blank if two \\{recto} or two \\{verso} pages follow each
  5175. other.  This information will already have been put into |this_page_recto|,
  5176. when the paper tray was selected.
  5177.  
  5178. For efficiency reasons, when printing in a duplex mode on the DEClaser~2200
  5179. (LN06), we switch into simplex mode for a \\{recto} page which is followed
  5180. by another such: this information was recorded in the array |blank_follows|
  5181. when the starting page was being located.  This speeds up processing,
  5182. because the printer doesn't need to perform the mechanical reversal of the
  5183. page only to find that is has nothing to be printed upon it.
  5184.  
  5185. @<Skip to a \\{recto} page, if necessary@>=
  5186. @<Reset duplexing, if appropriate@>;
  5187. if print_mode>=true_duplex_normal then
  5188. begin
  5189.   if last_page_recto=this_page_recto then
  5190.   begin
  5191.     if (print_mode=ln03_master) or not this_page_recto then
  5192.       write(ln3_file,' ',FF)  {Eject an extra page on LN03, or make
  5193.                                \\{recto} page blank}
  5194.   end else
  5195.   if (print_mode<ln03_master) and (ptr_blanks>0) and
  5196.       duplex_by_page_numbers and this_page_recto then
  5197.     if blank_follows[ptr_blanks]=cur_loc_after_bop then
  5198.     begin {This page has blank \\{verso} side}
  5199.       write(ln3_file,csi,true_simplex_normal:1,' x');
  5200.       duplex_to_be_reset:=true;
  5201.       decr(ptr_blanks)
  5202.     end
  5203. end;
  5204. last_page_recto:=this_page_recto;
  5205.  
  5206. @ If the previous page was printed in simplex mode, because it was a
  5207. \\{recto} page to be followed by another one then when we meet that next
  5208. \\{recto} page we have a look to see if the following page is yet another
  5209. \\{recto} one.  If not, we have to reset the required duplexing mode.
  5210.  
  5211. @<Reset duplexing, if appropriate@>=
  5212. if duplex_to_be_reset then {previous page was temporary excursion into
  5213.                             simplex mode}
  5214.   if ptr_blanks>0 then
  5215.     if blank_follows[ptr_blanks]=cur_loc_after_bop then
  5216.     begin
  5217.       decr(ptr_blanks); {and remain in simplex mode}
  5218.       if print_mode>=duplex_master_normal then
  5219.         write(ln3_file,' ',FF) {eject blank page for \\{verso} master}
  5220.     end else
  5221.     begin
  5222.       write(ln3_file,csi,print_mode:1,' x');
  5223.       duplex_to_be_reset:=false
  5224.     end
  5225.  
  5226. @ When we come to the end of the document when printing in any duplexing
  5227. mode, we may just have printed a \\{recto} page; we must either eject the
  5228. second side as blank (in true duplex modes) or print an extra blank page (in
  5229. any of the master modes).  This is accomplished by printing a form-feed, and
  5230. following it with a space; the form-feed on its own would just result in the
  5231. printer symbiont suppressing that which it would have inserted, but the
  5232. space fools it into sending a second form-feed itself.
  5233.  
  5234. @<Conditionally eject last page@>=
  5235. if (print_mode>=true_duplex_normal) and last_page_recto then
  5236.   if (print_mode>=duplex_master_normal) or not duplex_to_be_reset then
  5237.     write(ln3_file,FF,' ')
  5238.  
  5239. @ Here are the variables required to control \\{recto}/\\{verso} printing
  5240.  
  5241. @<Glob...@>=
  5242. @!last_page_recto:boolean; {|true| if previous page was \\{recto}}
  5243. @!this_page_recto:boolean; {used to save multiple evaluation of |recto_page|}
  5244. @!pages_printed:integer;   {counts each page generated for
  5245.                                 \.{/noduplex\_by\_page\_numbers}}
  5246. @!duplex_to_be_reset:boolean; {Used to reset any duplex mode after one sheet
  5247.                                has been printed in simplex}
  5248. @!cur_loc_after_bop:integer;  {Records |cur_loc| after the current page's
  5249.                                |bop| command was processed}
  5250.                         
  5251. @ These variables have to be initialized, of course
  5252.  
  5253. @<Set initial...@>==
  5254. pages_printed:=0; last_page_recto:=false; duplex_to_be_reset:=false;
  5255.  
  5256. @ When we first start writing the output file, we have to add commands to
  5257. tell the DEClaser printers from which tray to take the first sheet of paper,
  5258. and which printing mode to use.  The control sequence ending in |' x'| is
  5259. the |DECSPDM| (Set Duplex Print Mode) command.
  5260.  
  5261. @<Select printing mode and feed tray on DEClaser printers@>=
  5262. if first_tray<>default_tray then
  5263.   select_tray(first_tray);
  5264. if (print_mode>default_print_mode) and (print_mode<ln03_master) then
  5265.   write_ln(ln3_file,csi,print_mode:1,' x');
  5266.  
  5267. @*Procedures for recognizing and manipulating strings.  We have a
  5268. requirement for recognizing various ASCII strings in this program.  Some of
  5269. these strings specify functionality required from a \.{\BS special} command,
  5270. whilst others are the two-letter abbreviations used to specify the units of
  5271. dimensions: such dimensions can appear within \.{\BS special} commands and
  5272. also in some command line qualifiers.
  5273.  
  5274. The following data structures support the recognition of the strings used:
  5275. for commands, we need the following texts:
  5276.  
  5277. \yskip\hang\.{sx\ } ``Old-style'' introducer for inclusion of an LN03 sixel
  5278. file within the program's output.  Note that support for this command will
  5279. be removed in a future release following V4.0.
  5280.  
  5281. \yskip\hang\.{landscape} Output the following information in landscape orientation.
  5282.  
  5283. \yskip\hang\.{portrait} Output the following information in portrait orientation.
  5284.  
  5285. \yskip\hang\.{ln03:} New-style introducer for all LN03-specific \.{\BS
  5286. special}s.  All the remaining command strings are to be found following this
  5287. introducer.
  5288.  
  5289. \yskip\hang\.{sixel\ } Command to include a pixel file into the program's
  5290. output.
  5291.  
  5292. \yskip\hang\.{plotfile\ } Alternative name to include a sixel file.
  5293.  
  5294. \yskip\hang\.{defpoint\ } Define the coordinates of a point on the page:
  5295. used in conjunction with \.{connect} to draw changebars.
  5296.  
  5297. \yskip\hang\.{connect\ } Draw a changebar line between a pair of previously
  5298. defined points.
  5299.  
  5300. \yskip\hang\.{resetpoints\ } Clear all recorded details for a specified
  5301. range of named points.
  5302.  
  5303. \noindent Dimensions recognized are as in \TeX, with the addition of \.{px}
  5304. (representing a dimension unit of one pixel) and \.{mi} (representing one
  5305. micrometre or micron).
  5306.  
  5307. The creation of these strings could have been much cleaner through use of
  5308. {W\sc EB}'s string pool feature, but it was felt that it was nevertheless
  5309. better to avoid the use of the auxiliary \.{.POOL} file.
  5310.  
  5311. @<Types...@>=
  5312. @!pool_pointer = 0..pool_size;
  5313. @!str_number = 0..max_strings;
  5314.  
  5315. @ @<Globals...@>=
  5316. @!str_pool : packed array [pool_pointer] of ASCII_code;
  5317. @!str_start : array [str_number] of pool_pointer;
  5318. @!pool_ptr : pool_pointer; {First unused position in |str_pool|}
  5319. @!str_ptr : str_number;   {Number of current string being created}
  5320.  
  5321. @ Here's how we initialize all the strings for recognition.  Note that
  5322. string $0$ is reserved for the null string.  The macro definitions allow us
  5323. to refer to the various strings symbolically; note that the related numbers
  5324. will need to be changed if the order of initialization below is altered!
  5325. |min_string_match| specifies the minimum number of characters which are
  5326. required to match when the string being compared has been specified in
  5327. truncated form; at present, all strings are unique in their first two
  5328. characters.
  5329.  
  5330. @d min_string_match=2
  5331. @#
  5332. @d dimen_pt=1
  5333. @d dimen_mm=dimen_pt+1
  5334. @d dimen_in=dimen_mm+1
  5335. @d dimen_px=dimen_in+1
  5336. @d dimen_cm=dimen_px+1
  5337. @d dimen_pc=dimen_cm+1
  5338. @d dimen_bp=dimen_pc+1
  5339. @d dimen_dd=dimen_bp+1
  5340. @d dimen_cc=dimen_dd+1
  5341. @d dimen_sp=dimen_cc+1
  5342. @d dimen_mi=dimen_sp+1
  5343. @d spec_sx=dimen_mi+1
  5344. @d spec_land=spec_sx+1
  5345. @d spec_port=spec_land+1
  5346. @d spec_ln=spec_port+1
  5347. @d spec_six=spec_ln+1
  5348. @d spec_plt=spec_six+1
  5349. @d spec_set=spec_plt+1
  5350. @d spec_con=spec_set+1
  5351. @d spec_rst=spec_con+1
  5352.  
  5353. @<Set initial...@>=
  5354. pool_ptr := 0; str_ptr := 0; str_start[0] := 0;
  5355. make_string('pt');
  5356. make_string('mm');
  5357. make_string('in');
  5358. make_string('px');
  5359. make_string('cm');
  5360. make_string('pc');
  5361. make_string('bp');
  5362. make_string('dd');
  5363. make_string('cc');
  5364. make_string('sp');
  5365. make_string('mi');
  5366. make_string('sx ');
  5367. make_string('landscape');
  5368. make_string('portrait');
  5369. make_string('ln03:');
  5370. make_string('sixel ');
  5371. make_string('plotfile ');
  5372. make_string('defpoint ');
  5373. make_string('connect ');
  5374. make_string('resetpoints ');
  5375. incr(str_ptr); str_start[str_ptr]:=pool_ptr;
  5376.  
  5377. @ Here's the procedure for putting the strings into the pool.  We make use
  5378. here  of a couple of auxiliary macros for adding characters to the string
  5379. pool, and another for reporting errors with the arguments of a \.{\BS
  5380. special}.
  5381.  
  5382. @d print_spec == for spec_ptr:= 1 to spec_len-1 do
  5383.                  begin print(xchr[spec_par[spec_ptr]]);
  5384.                        write(term_out,xchr[spec_par[spec_ptr]])
  5385.                  end;
  5386.                  print(''''); write(term_out,'''')
  5387. @d spec_error(#) == begin error(#,' `'); print_spec end
  5388.  
  5389. @d append_char(#) == {Put |ASCII_code| \# at the end of the |str_pool|}
  5390.   begin
  5391.     str_pool[pool_ptr] := #;
  5392.     incr(pool_ptr)
  5393.   end
  5394. @d str_room(#) == {make sure that |str_pool| can take a string of length \#}
  5395.   begin
  5396.     if pool_ptr + # > pool_size then
  5397.       abort('Internal error (String pool overflow)')
  5398. @:fatal error Internal error String pool overflow}{\qquad\.{(String pool overflow)}@>
  5399.   end
  5400.  
  5401. @<Procedures for...@> =
  5402. procedure make_string(@!s : packed array [@!low..@!high : integer] of char);
  5403. var
  5404.   @!i : integer;
  5405. begin
  5406.   str_room(high-low+1);
  5407.   incr(str_ptr);
  5408.   str_start[str_ptr] := pool_ptr;
  5409.   for i := low to high do append_char(xord[s[i]]);
  5410. end;
  5411.  
  5412. @ This module has a set of functions (and one procedure) in it that are
  5413. generally used in decoding \.{\BS special} commands.  All these procedures,
  5414. together with those in the following two sections, are ``low-level'' and
  5415. included at an early point in the \PASCAL\ so that they are available to all
  5416. routines provided through {W\mc EB}'s \.{@@p} directive.
  5417.  
  5418. The functions are:
  5419. \item{$\bullet$} |rd_char|, which reads a character from |spec_par| at
  5420. |spec_ptr|,
  5421. \item{$\bullet$} |rd_nb_char|, which reads a non-blank character, using
  5422. |rd_char|,
  5423. \item{$\bullet$} |rd_int|, which reads an integer, using the two |rd_char|s,
  5424. \item{$\bullet$} |rd_real|, which reads a `real' number, but without exponent.
  5425.  
  5426. All the functions return a |boolean|, which is |true| if they `failed' (which,
  5427. in the case of the |rd_char|s, means they got to the end of the line).
  5428. |rd_real| accepts |'.'| as `0.0' (!). (Personally, I wouldn't give you twopence
  5429. for these number routines: they have several curious properties, including the
  5430. pleasing way they collapse when presented with an isolated minus-sign!)
  5431. [This comment was made by Robin Fairbairns
  5432. @^Fairbairns, Robin@>
  5433. of LaserScan (Cambridge UK), who wrote them and provided the other features
  5434. that support \.{\\changebar} commands through appropriate \.{\\special}
  5435. strings.]
  5436.  
  5437. The one procedure in the module is |bksp_char|, which backspaces by one
  5438. character, except, of course, at the beginning of the buffer.
  5439.  
  5440. @<Additional low...@>=
  5441. function rd_char (var ch : [volatile] ASCII_code) : boolean;
  5442. begin
  5443. if spec_ptr>spec_len
  5444. then begin ch:=" "; rd_char:=true; end
  5445. else begin ch:=spec_par[spec_ptr]; incr(spec_ptr); rd_char:=false; end;
  5446. end;
  5447.  
  5448. function rd_nb_char (var ch : [volatile] ASCII_code) : boolean;
  5449. var
  5450. @! end_of_line : boolean;
  5451. begin
  5452. repeat end_of_line := rd_char(ch) until end_of_line or (ch<>" ");
  5453. rd_nb_char := end_of_line;
  5454. end;
  5455.  
  5456. procedure bksp_char;
  5457. begin
  5458. if spec_ptr>1 then decr(spec_ptr);
  5459. end;
  5460.  
  5461. function rd_int ( var res : [volatile] integer ) : boolean;
  5462. var
  5463. @!ch : ASCII_code;
  5464. @!end_of_line,negative : boolean;
  5465. begin
  5466. res := 0; negative := false;
  5467. end_of_line:=rd_nb_char(ch);
  5468. if ch="-" then begin
  5469.   negative := true;
  5470.   end_of_line := rd_char(ch);
  5471.   end;
  5472. if end_of_line then rd_int:=true else rd_int:=not (ch in ["0".."9"]);
  5473. while (not end_of_line) and (ch in ["0".."9"]) do begin
  5474.   res:=res*10+(ch-"0");
  5475.   end_of_line:=rd_char(ch);
  5476.   end;
  5477. if not end_of_line then bksp_char;
  5478. if negative then res:=-res;
  5479. end;
  5480.  
  5481. function rd_real (var res : [volatile] real ) : boolean;
  5482. var
  5483. @!ch : ASCII_code;
  5484. @!end_of_line,negative:boolean;
  5485. @!in_fract:boolean;
  5486. @!fract:real;
  5487. @!dec_pt:real;
  5488.  
  5489. begin
  5490. res:=0.0; fract:=0.0; dec_pt:=1.0; negative := false;
  5491. end_of_line:=rd_nb_char(ch);
  5492. if ch="-" then begin
  5493.   negative := true;
  5494.   end_of_line := rd_char(ch);
  5495.   end;
  5496. if end_of_line then rd_real:=true
  5497. else begin
  5498.   rd_real:=false; {initial estimate}
  5499.   if ch in ["0".."9"]
  5500.   then in_fract:=false
  5501.   else if ch="."
  5502.   then begin
  5503.     in_fract:=true;
  5504.     end_of_line:=rd_char(ch);
  5505.     end
  5506.   else rd_real:=true;
  5507.   end;
  5508. while (not end_of_line) and (ch in ["0".."9"])
  5509. do begin
  5510.   if in_fract then begin
  5511.     dec_pt:=dec_pt*0.1;
  5512.     fract:=fract+dec_pt*(ch-"0");
  5513.     end
  5514.   else res:=res*10.0+(ch-"0");
  5515.   end_of_line:=rd_char(ch);
  5516.   if not(end_of_line or in_fract) then
  5517.     if ch="." then begin
  5518.       in_fract:=true;
  5519.       end_of_line:=rd_char(ch);
  5520.       end;
  5521.   end;
  5522. if not end_of_line then bksp_char;
  5523. if negative then res:=-res-fract else res:=res+fract;
  5524. end;
  5525.  
  5526. @ Here is a function which matches characters from the buffer into which
  5527. the string associated with the \.{\BS special} command is read against those
  5528. strings in the |str_pool|.  It returns the index number of that particular
  5529. string.  It is not necessary for the whole stored string to be matched; it
  5530. will be noted that all strings are unique in their first two characters;
  5531. provided at least these characters are matched \&{and} the first non-matched
  5532. character in the \.{\BS special} command is a space character, then a match
  5533. will still be found. The function returns a pointer to string $0$ (the null
  5534. string) if no match is found.
  5535.  
  5536. Here is a macro which allows us to find the length of a string in the pool:
  5537.  
  5538. @d str_length(#) == (str_start[#+1]-str_start[#])
  5539.  
  5540. @<Additional low...@>=
  5541. function find_match : str_number;
  5542. var
  5543.   @!i,@!l : integer;
  5544.   @!m : str_number;
  5545.   @!chr_ptr : pool_pointer;
  5546.   @!eol,@!found : boolean;
  5547. begin
  5548.   eol := false;  {We start by skipping over any leading spaces}
  5549.   while (spec_par[spec_ptr] = " ") and not eol do
  5550.     if spec_ptr >= spec_len then
  5551.       eol := true
  5552.     else
  5553.       incr(spec_ptr);
  5554.   m := 0;    {Assume the worst!}
  5555.   if eol then spec_error('Unsupported or malformed \special ')
  5556. @:Error: Unsupported or malformed special}{\quad\.{Unsupported or malformed \\special}@>
  5557.   else
  5558.   begin
  5559.     i := spec_ptr;  {Point into the body of the \.{\BS special} command}
  5560.     m := dimen_pt;
  5561.     found := false;
  5562.     repeat
  5563.       l := str_length(m);
  5564.       chr_ptr := str_start[m];
  5565.       while ((spec_par[i] = str_pool[chr_ptr]) or
  5566.             (spec_par[i] = str_pool[chr_ptr]+"A"-"a")) and
  5567.             not eol and not found do
  5568.       begin
  5569.         decr(l);
  5570.         if l=0 then found := true else
  5571.         begin
  5572.           if spec_ptr >= spec_len then eol := true
  5573.           else
  5574.             incr(i);
  5575.           incr(chr_ptr);
  5576.         end;
  5577.       end;
  5578.       if eol or (spec_par[i] = " ") then
  5579.         if i-spec_ptr > min_string_match then found := true;
  5580.       if not found then
  5581.       begin
  5582.         i := spec_ptr; incr(m);
  5583.       end
  5584.     until found or (m > str_ptr);
  5585.     if not found then m := 0
  5586.     else spec_ptr:=i+1;
  5587.   end;
  5588.   find_match := m;
  5589. end;
  5590.  
  5591. @ This module defines a `derived' function for reading, as an integer number of
  5592. pixels, a `dimension' from the \.{\BS special}'s arguments (where `dimension'
  5593. is---roughly---what one gets in \TeX).  It firstly reads a \meta{real number}
  5594. from the argument string, and then multiplies this by an appropriate factor
  5595. determined by the following two character string, which should be one of \TeX's
  5596. \meta{physical unit} designators (\.{pt}, \.{bp}, etc) with the addition of
  5597. \.{px}, denoting pixels, and \.{mi} for microns ($\mu\hbox{m}$).
  5598.  
  5599. The function has a |boolean| result, which once again is |true| if it fails to
  5600. find what was expected.
  5601.  
  5602. @d spec_warning(#)==
  5603.     begin
  5604.       warning(#,' `'); print_spec
  5605.     end
  5606.  
  5607. @<Additional low...@>=
  5608. function rd_dimension ( var res : [volatile] integer ) : boolean;
  5609. var
  5610. @! dimen_found : str_number;
  5611. @! real_value : real;
  5612. begin
  5613. if rd_real(real_value) then rd_dimension:=true
  5614. else begin
  5615.   rd_dimension:=false;
  5616.   dimen_found := find_match;
  5617.   case dimen_found of
  5618.     dimen_pt: real_value:=real_value*resolution/72.27;
  5619.     dimen_in: real_value:=real_value*resolution;
  5620.     dimen_px: do_nothing;
  5621.     dimen_pc: real_value:=real_value*12.0*resolution/72.27;
  5622.     dimen_cm: real_value:=real_value*resolution/2.54;
  5623.     dimen_mm: real_value:=real_value*resolution/25.4;
  5624.     dimen_bp: real_value:=real_value*resolution/72.0;
  5625.     dimen_dd: real_value:=real_value*(1238.0/1157.0)*resolution/72.27;
  5626.     dimen_cc: real_value:=real_value*(14856.0/1157.0)*resolution/72.27;
  5627.     dimen_mi: real_value:=real_value*resolution/25400.0;
  5628.     dimen_sp: real_value:=real_value*resolution/65536.0/72.27;
  5629.     othercases
  5630.       begin
  5631.         spec_warning('Missing or unknown unit of measure:');
  5632. @:Warning: Missing or unknown unit of measure}{\quad\.{Missing or unknown unit of measure}@>
  5633.         rd_dimension:=true; {Assumes value is in pixels}
  5634.       end;
  5635.   endcases;
  5636. end;
  5637. res := round(real_value);
  5638. end;
  5639.  
  5640. @ This analogous function reads a dimension and converts it to scaled
  5641. points; this is used by the \.{/hfuzz} and \.{/vfuzz} qualifiers.
  5642.  
  5643. @<Additional low...@>=
  5644. function rd_scaled_pt ( var res : [volatile] integer ) : boolean;
  5645. var
  5646. @! dimen_found : str_number;
  5647. @! real_value : real;
  5648. begin
  5649. if rd_real(real_value) then rd_scaled_pt:=true
  5650. else begin
  5651.   rd_scaled_pt:=false;
  5652.   dimen_found := find_match;
  5653.   case dimen_found of
  5654.     dimen_pt: real_value:=real_value*65536.0;
  5655.     dimen_in: real_value:=real_value*65536.0*72.27;
  5656.     dimen_px: real_value:=real_value/conv;
  5657.     dimen_pc: real_value:=real_value*12.0*65536.0;
  5658.     dimen_cm: real_value:=real_value*65536.0*72.27/2.54;
  5659.     dimen_mm: real_value:=real_value*65536.0*72.27/25.4;
  5660.     dimen_bp: real_value:=real_value*65536.0*72.27/72.0;
  5661.     dimen_dd: real_value:=real_value*(1238.0/1157.0)*65536.0;
  5662.     dimen_cc: real_value:=real_value*(14856.0/1157.0)*65536.0;
  5663.     dimen_mi: real_value:=real_value*65536.0*72.27/25400.0;
  5664.     dimen_sp: do_nothing;
  5665.     othercases
  5666.       begin
  5667.         spec_warning('Missing or unknown unit of measure:');
  5668. @:Warning: Missing or unknown unit of measure}{\quad\.{Missing or unknown unit of measure}@>
  5669.         rd_scaled_pt:=true; {Assumes value is in sp}
  5670.       end;
  5671.   endcases;
  5672. end;
  5673. res := round(real_value);
  5674. end;
  5675.  
  5676. @ Collection of rules for a page is fairly trivial, but it's now called from
  5677. three places, so we make it a procedure. All rules are collected and placed
  5678. into |page_rules|. Later, the rules will be merged.
  5679.  
  5680. @p procedure Collect_rule(hhh, vvv, wwidth, hheight : integer);
  5681. begin
  5682. if rules_on_page<max_rules then incr(rules_on_page) else
  5683. if not rule_limit_exceeded then begin
  5684. warning('more than ',max_rules:1,' rules on page; excess ignored!');
  5685. @.more than ... rules@>
  5686. rule_limit_exceeded:=true end;
  5687. with page_rules[rules_on_page] do begin
  5688.         horiz := hhh; vert := vvv; height := hheight;
  5689.         width := wwidth; covered := false;
  5690.           end;
  5691. end;
  5692.  
  5693. @ This module defines three procedures:
  5694.  
  5695. \yskip\hang$\bullet$ |reset_points|, which resets the |present| flags of a
  5696. range of |point|s,
  5697.  
  5698. \yskip\hang$\bullet$ |define_point|, which gives a value to a |point|, and
  5699.  
  5700. \yskip\hang$\bullet$ |connect_points|, which makes connection between two
  5701. |point|s.
  5702.  
  5703. The macro simplifies the task of finding the next comma in the \.{\BS
  5704. special} string.
  5705.  
  5706. @d find_comma ==   repeat end_of_line:=rd_nb_char(ch)
  5707.   until end_of_line or (ch<>",")
  5708.  
  5709. @p procedure reset_points;
  5710. var
  5711. @!low,high : integer;
  5712. begin
  5713. rd_int(low); rd_int(high);
  5714. if (0<low) and (low<=high) and (high<=max_points)
  5715. then begin
  5716.   for k:=low to high do point[k].present:=false;
  5717.   end
  5718. else if (0<low) and (low<=max_points)
  5719. then point[low].present:=false;
  5720. end;
  5721.  
  5722. procedure define_point;
  5723. var
  5724. @! which : integer; {the point number}
  5725. @! h_val, v_val : integer; {its coordinates}
  5726. @! ch : ASCII_code;
  5727. @! end_of_line : boolean;
  5728. begin
  5729. if rd_int(which) then
  5730.   spec_error('Point number missing in \special ')
  5731. @:Error: Point number missing in special}{\quad\.{Point number missing in \\special}@>
  5732. else if (which<=0) or (which>max_points) then
  5733.   spec_error('Invalid point number in \special ')
  5734. @:Error: Invalid point number in special}{\quad\.{Invalid point number in \\special}@>
  5735. else begin
  5736.   find_comma;
  5737.   if ch="("
  5738.   then begin
  5739.     if rd_dimension(h_val)
  5740.     then begin
  5741.       h_val:=hh;
  5742.       v_val:=vv;
  5743.       end
  5744.     else begin
  5745.       if rd_nb_char(ch) then do_nothing;
  5746.       if ch=","
  5747.       then begin
  5748.         if rd_dimension(v_val) then v_val:=vv;
  5749.         end
  5750.       else v_val:=vv;
  5751.       end;
  5752.     end
  5753.   else
  5754.     begin
  5755.     h_val:=hh; v_val:=vv;
  5756.     end;
  5757.   {now we've got |h_val|, |v_val| and |which| all set (to something!)}
  5758.   with point[which] do
  5759.   begin hh := h_val; vv := v_val; present := true end;
  5760.   end;
  5761. end;
  5762.  
  5763. procedure connect_points;
  5764. var
  5765. @! a1,b1, a2,b2 : integer;
  5766. @! width : integer;
  5767. @! half_width : integer;
  5768. @! end_of_line, bad_point : boolean;
  5769. @! ch : ASCII_code;
  5770. begin
  5771. end_of_line:=rd_int(a1);
  5772. end_of_line:=rd_nb_char(ch);
  5773. if ch="/"
  5774. then end_of_line:=rd_int(b1)
  5775. else begin
  5776.   if not end_of_line then bksp_char; b1:=a1;
  5777.   end;
  5778. find_comma;
  5779. if not end_of_line then bksp_char;
  5780. end_of_line:=rd_int(a2);
  5781. end_of_line:=rd_nb_char(ch);
  5782. if ch="/"
  5783. then end_of_line:=rd_int(b2)
  5784. else begin
  5785.   if not end_of_line then bksp_char; b2:=a2;
  5786.   end;
  5787.  
  5788. find_comma;
  5789. if not end_of_line then bksp_char;
  5790. if rd_dimension(width) then width:=2; {default dimension is 2 pixels, ~0.4 pts}
  5791. if odd(count[0])
  5792. then begin b1:=a1; b2:=a2; end;
  5793.  
  5794. {check the point numbers}
  5795. if (b1<=0) or (b1>max_points) or (b2<=0) or (b2>max_points)
  5796. then bad_point:=true
  5797. else bad_point:=false;
  5798. if not bad_point then begin
  5799.   if (not point[b1].present) or (not point[b2].present)
  5800.   then bad_point:=true;
  5801.   end;
  5802. if bad_point then
  5803.   spec_error('Invalid point number in \special ')
  5804. @:Error: Invalid point number in special}{\quad\.{Invalid point number...}@>
  5805. else begin
  5806.   half_width := width div 2;
  5807.   if point[b1].hh=point[b2].hh
  5808.   then Collect_rule(point[b1].hh-half_width,
  5809.                     VAX_max(point[b1].vv,point[b2].vv),
  5810.                     width,
  5811.                     abs(point[b2].vv-point[b1].vv))
  5812.   else if point[b1].vv=point[b2].vv
  5813.   then Collect_rule(VAX_min(point[b1].hh,point[b2].hh),
  5814.                     point[b1].vv+half_width,
  5815.                     abs(point[b1].hh-point[b2].hh),
  5816.                     width)
  5817.   else spec_error('Diagonal rule in \special ');
  5818. @:Error: Diagonal rule in special}{\quad\.{Diagonal rule in \\special}@>
  5819.   end;
  5820. end;
  5821.  
  5822.  
  5823. @* Translation to symbolic form.
  5824. The main work of \.{DVItoLN03} is accomplished by the |do_page| procedure,
  5825. which produces the output for an entire page, assuming that the |bop|
  5826. command for that page has already been processed. This procedure is
  5827. essentially an interpretive routine that reads and acts on the \.{DVI}
  5828. commands.
  5829.  
  5830. @ The definition of \.{DVI} files refers to six registers,
  5831. $(h,v,w,x,y,z)$, which hold integer values in \.{DVI} units.  In practice,
  5832. we also need registers |hh| and |vv|, the pixel analogs of $h$ and $v$,
  5833. since it is not always true that |hh=pixel_round(h)| or
  5834. |vv=pixel_round(v)|.  Note that the current font is neither stacked
  5835. nor restored by |push| and |pop| commands.
  5836.  
  5837. The stack of $(h,v,w,x,y,z)$ values is represented by eight arrays
  5838. called |hstack|, \dots, |zstack|, |hhstack|, and |vvstack|.
  5839.  
  5840. @<Glob...@>=
  5841. @!h,@!v,@!w,@!x,@!y,@!z,@!hh,@!vv:integer; {current state values}
  5842. @!hstack,@!vstack,@!wstack,@!xstack,@!ystack,@!zstack:
  5843.   array [0..stack_size] of integer; {pushed down values in \.{DVI} units}
  5844. @!hhstack,@!vvstack:
  5845.   array [0..stack_size] of integer; {pushed down values in pixels}
  5846.  
  5847. @ Three characteristics of the pages (their |max_v|, |max_h|, and
  5848. |max_s|) are specified in the postamble, and a warning message
  5849. is printed if these limits are exceeded. Actually |max_v| is set to
  5850. the maximum height plus depth of a page, and |max_h| to the maximum width,
  5851. for purposes of page layout. Since characters can legally be set outside
  5852. of the page boundaries, it is not an error when |max_v| or |max_h| is
  5853. exceeded. But |max_s| should not be exceeded.
  5854.  
  5855. The postamble also specifies the total number of pages; \.{DVItoLN03}
  5856. checks to see if this total is accurate.
  5857.  
  5858. @<Glob...@>=
  5859. @!max_v:integer; {the value of |abs(v)| should probably not exceed this}
  5860. @!max_h:integer; {the value of |abs(h)| should probably not exceed this}
  5861. @!max_h_saved,@!max_v_saved:integer; {`Static' value for oversize messages}
  5862. @!max_s:integer; {the stack depth should not exceed this}
  5863. @!max_v_so_far,@!max_h_so_far,@!max_s_so_far:integer; {the record high levels}
  5864. @!total_pages:integer; {the stated total number of pages}
  5865. @!page_count:integer; {the total number of pages seen so far}
  5866.  
  5867. @ @<Set init...@>=
  5868. max_v:=@'17777777777-99; max_h:=@'17777777777-99; max_s:=stack_size+1;@/
  5869. max_v_so_far:=0; max_h_so_far:=0; max_s_so_far:=0; page_count:=0;
  5870.  
  5871. @ Here is a procedure which will be required when decoding |xxx| commands.
  5872. If the string of characters in a \TeX\ \.{\\special} command commences with
  5873. the character string |'SX '| (i.e., `old style'), or |'ln03:plotfi[le] '| or
  5874. |'ln03:sixel '| (`new style'), the following procedure will  copy the
  5875. remainder of the \.{\\special} command's parameter string (which is assumed
  5876. to be the name of a VAX/VMS file containing a graphics image encoded into
  5877. DEC's standard ``sixel'' format) into the |text| field of a |segment|; the
  5878. remaining fields are used to record the page co\"ordinates at which the dump
  5879. shall be placed.
  5880.  
  5881. @p procedure include_sixel_dump;
  5882. begin
  5883.   finish_seg; {ensure that we have no |cur_seg|}
  5884.   new_segment(true,true,0,0); {|font_code| and |ch_code| irrelevant}
  5885.   with cur_seg^ do
  5886.   begin
  5887.     seg_type:=sixel_dump;   {that's the flavour of segment}
  5888.     text:='';
  5889.     while spec_ptr <= spec_len do
  5890.     begin
  5891.       text := text + xchr[spec_par[spec_ptr]];
  5892.       incr(spec_ptr)
  5893.     end;
  5894.     xs:=x_pos; {record starting position of dump's placement}
  5895.   end;
  5896.   finish_seg {detach the segment; it's attached to correct |seg_list|}
  5897. end;
  5898.  
  5899. @ Before we get into the details of |do_page|, it is convenient to
  5900. consider a simpler routine that computes the first parameter of each
  5901. opcode.
  5902.  
  5903. @p function first_par(o:eight_bits):integer;
  5904. begin case o of
  5905. sixty_four_cases(set_char_0),sixty_four_cases(set_char_0+64):
  5906.   first_par:=o-set_char_0;
  5907. set1,put1,fnt1,xxx1,fnt_def1: first_par:=get_byte;
  5908. set1+1,put1+1,fnt1+1,xxx1+1,fnt_def1+1: first_par:=get_two_bytes;
  5909. set1+2,put1+2,fnt1+2,xxx1+2,fnt_def1+2: first_par:=get_three_bytes;
  5910. right1,w1,x1,down1,y1,z1: first_par:=signed_byte;
  5911. right1+1,w1+1,x1+1,down1+1,y1+1,z1+1: first_par:=signed_pair;
  5912. right1+2,w1+2,x1+2,down1+2,y1+2,z1+2: first_par:=signed_trio;
  5913. set1+3,set_rule,put1+3,put_rule,right1+3,w1+3,x1+3,down1+3,y1+3,z1+3,
  5914.   fnt1+3,xxx1+3,fnt_def1+3: first_par:=signed_quad;
  5915. nop,bop,eop,push,pop,pre,post,post_post,undefined_commands: first_par:=0;
  5916. w0: first_par:=w;
  5917. x0: first_par:=x;
  5918. y0: first_par:=y;
  5919. z0: first_par:=z;
  5920. sixty_four_cases(fnt_num_0): first_par:=o-fnt_num_0;
  5921. end;
  5922. end;
  5923.  
  5924. @ Here is another subroutine that we need: It computes the number of
  5925. pixels in the height or width of a rule. Characters and rules will line up
  5926. properly if the sizes are computed precisely as specified here.  (Since
  5927. |conv| is computed with some floating-point roundoff error, in a
  5928. machine-dependent way, format designers who are tailoring something for a
  5929. particular resolution should not plan their measurements to come out to an
  5930. exact integer number of pixels; they should compute things so that the
  5931. rule dimensions are a little less than an integer number of pixels, e.g.,
  5932. 4.99 instead of 5.00.)
  5933.  
  5934. @p function rule_pixels(x:integer):integer;
  5935.   {computes $\lceil|conv|\cdot x\rceil$}
  5936. var n:integer;
  5937. begin n:=trunc(conv*x);
  5938. if n<conv*x then rule_pixels:=n+1 @+ else rule_pixels:=n;
  5939. end;
  5940.  
  5941. @ Strictly speaking, the |do_page| procedure is really a function with
  5942. side effects, not a `\&{procedure}'\thinspace; it returns the value |false|
  5943. if \.{DVItoLN03} should be aborted because of some unusual happening. The
  5944. subroutine is organized as a typical interpreter, with a multiway branch
  5945. on the command code followed by |goto| statements leading to routines that
  5946. finish up the activities common to different commands. We will use the
  5947. following labels:
  5948.  
  5949. @d fin_set=41 {label for commands that set or put a character}
  5950. @d fin_rule=42 {label for commands that set or put a rule}
  5951. @d move_right=43 {label for commands that change |h|}
  5952. @d move_down=44 {label for commands that change |v|}
  5953. @d change_font=45 {label for commands that change |cur_font|}
  5954.  
  5955. @ Some \PASCAL\ compilers severely restrict the length of procedure bodies,
  5956. so we shall split |do_page| into two parts, one of which is
  5957. called |special_cases|. The different parts communicate with each other
  5958. via the global variables mentioned above, together with the following ones:
  5959.  
  5960. @<Glob...@>=
  5961. @!s:integer; {current stack size}
  5962. @!cur_font:integer; {current internal font number}
  5963. @!cur_base:integer; {$\equiv$|glyph_base[cur_font]|, to index into |glyphs|}
  5964.  
  5965. @ Here is the overall setup.  The parameter is an extension from the
  5966. original program to accommodate virtual font character sequences; in normal
  5967. operation (when processing bytes from the \.{DVI} file), this parameter is
  5968. negative, and |do_page| processes one complete page of the file.  If a
  5969. character being set on the current page is taken from a virtual font, and
  5970. doesn't simply map to a single physical character from some other font, then
  5971. |do_page| will be called recursively, with a parameter that points to the
  5972. first byte of a sequence of |dvi| bytes which implements the virtual
  5973. character.
  5974.  
  5975. @p @t\4@>@<Declare the function called |special_cases|@>@;
  5976. @t\4@>@<Declare the function called |reduce_rules|@>@;
  5977. function do_page; {|do_page(@!vf_start:integer):boolean;|}
  5978. label fin_set,fin_rule,move_right,done,9998,9999;
  5979. var o:eight_bits; {operation code of the current command}
  5980. @!p,@!q:integer; {parameters of the current command}
  5981. @!a:integer; {byte number of the current command}
  5982. @!hhh:integer; {|h|, rounded to the nearest pixel}
  5983. @!saved_font:integer; {implicit |push| around virtual font character}
  5984. @!old_take:integer; {ditto for taking |dvi| from array |vf|}
  5985. begin
  5986.   if vf_start < 0 then
  5987.   begin {initialize the state variables if not recursing}
  5988.     s:=0; h:=0; v:=0; w:=0; x:=0; y:=0; z:=0; hh:=0; vv:=0;
  5989.   end;
  5990.   saved_font:=cur_font;  {for later restoration}
  5991.   cur_font:=nf; {set current font undefined}
  5992.   old_take:=vf_take;
  5993.   vf_take:=vf_start; {ensure |dvi| bytes taken from appropriate source}
  5994. while true do @<Translate the next command in the \.{DVI} file;
  5995.     |goto 9999| with |do_page=true| if it was |eop|;
  5996.     |goto 9998| if premature termination is needed@>;
  5997. 9998: print_ln('!'); do_page:=false;
  5998. 9999: cur_font:=saved_font;
  5999. cur_base:=glyph_base[cur_font];
  6000. vf_take:=old_take;
  6001. end;
  6002.  
  6003. @ Error messages are written to the log file with full details of their position
  6004. within the \.{DVI} file, and, in a shorter form, to the terminal.
  6005.  
  6006.  
  6007. @<Translate the next command...@>=
  6008. begin a:=cur_loc;
  6009. o:=get_byte; p:=first_par(o);
  6010. if eof(dvi_file) then bad_dvi('the file ended prematurely');
  6011. @:Bad DVI file the file ended prematurely}{\quad\.{the file ended prematurely}@>
  6012. @<Start translation of command |o| and |goto| the appropriate label to
  6013.   finish the job@>;
  6014. fin_set: @<Finish a command that either sets or puts a character, then
  6015.     |goto move_right| or |done|@>;
  6016. fin_rule: @<Finish a command that either sets or puts a rule, then
  6017.     |goto move_right| or |done|@>;
  6018. move_right: @<Finish a command that sets |h:=h+q|, then |goto done|@>;
  6019. done:
  6020. end
  6021.  
  6022. @ The multiway switch in |first_par|, above, was organized by the length
  6023. of each command; the one in |do_page| is organized by the semantics.
  6024.  
  6025. @<Start translation...@>=
  6026. if o<set_char_0+128 then @<Translate a |set_char| command@>
  6027. else case o of
  6028.   four_cases(set1): goto fin_set;
  6029.   four_cases(put1): goto fin_set;
  6030.   set_rule: goto fin_rule;
  6031.   put_rule: goto fin_rule;
  6032.   @t\4@>@<Cases for commands |nop|, |bop|, \dots, |pop|@>@;
  6033.   @t\4@>@<Cases for horizontal motion@>@;
  6034.   othercases if special_cases(o,p,a) then goto done@+else goto 9998
  6035.   endcases
  6036.  
  6037. @* Rule Accumulation.
  6038. We accumulate all rules generated on a page. At the end of the page,
  6039. the rules are combined to form larger rules when possible, then
  6040. output.
  6041.  
  6042. A special record describes the rule rectangles encountered.
  6043. @<Types...@>=
  6044. @!rules = record {rule record}
  6045. @!horiz,@; {Horizontal Coordinate}
  6046. @!width,@; {Width in Horizontal Direction}
  6047. @!vert,@; {Vertical Coordinate}
  6048. @!height:integer; {Height in Vertical Direction}
  6049. @!covered:boolean; {Indicates if combined}
  6050. end;
  6051.  
  6052. @ An array of |rules| is defined along with an index.
  6053. @<Glob...@>=
  6054. @!page_rules:array[1..max_rules] of rules; {rules generated on this page}
  6055. @!rules_on_page: 0..max_rules; {count of rules found on this page}
  6056. @!rule_limit_exceeded: boolean; {used to produce just one warning message}
  6057.  
  6058. @ Initialize |rules_on_page| to show no rules yet.
  6059. @<Set init...@>=
  6060. @!rules_on_page := 0; {set no rules found yet}
  6061. @!rule_limit_exceeded := false; {so we haven't got too many, either!}
  6062.  
  6063. @ This module used to contain the code for collecting rules, but now it's
  6064. limited to calling a procedure elsewhere that does the job.
  6065.  
  6066. @<Collect Rules for Page@>=
  6067. Collect_rule(hh, vv, rule_pixels(q), rule_pixels(p));
  6068.  
  6069. @ Rules can now be combined and output in the \.{LN3} file. To accomplish
  6070. this the rules are scanned in vertical and then in horizontal order. During
  6071. the scan adjacent rules are combined and marked as used. At completion, rules
  6072. that aren't marked as |covered| are output.
  6073.  
  6074. @<Combine and output Rules@>=
  6075. if rules_on_page > 0 then
  6076.               begin
  6077.               reduce_rules;
  6078.               for rules_on_page := rules_on_page downto 1 do
  6079.                 with page_rules[rules_on_page] do
  6080.                   if not covered then
  6081.                     write_ln(ln3_file,csi,'1;',horiz+left_marg:1,';',
  6082.                                 vert-height+top_marg:1,';',
  6083.                                 height:1,';',width:1,'!|');
  6084.               rules_on_page := 0
  6085.               end;
  6086.  
  6087. @ |reduce_rules| implements the rule reduction algorithm. Maps are generated
  6088. for traversing all rules both horizontally and vertically. Rules can overlap
  6089. or underlap by up to |max_lap_rules| in |height| or |width|.
  6090.  
  6091. @<Declare the function called |reduce_rules|@>=
  6092. procedure reduce_rules;
  6093. var
  6094. horiz_map: array[1..max_rules] of 0..max_rules;
  6095. verti_map: array[1..max_rules] of 0..max_rules;
  6096. hi,hj,vi,vj: 0..max_rules;
  6097. ht,vt:integer;
  6098. begin
  6099. for hi := 1 to rules_on_page do horiz_map[hi] := hi;
  6100. for vi := 1 to rules_on_page do verti_map[vi] := vi;
  6101. @<Build Horizontal Map@>@;
  6102. @<Traverse Horizontally@>@;
  6103. @<Build Vertical Map@>@;
  6104. @<Traverse Vertically@>@;
  6105. end;
  6106.  
  6107. @ Build map for horizontal combination by sorting the rules into
  6108. increasing vertical order and within the same vertical line into
  6109. increasing horizontal order. This allows for rule combination by
  6110. looking for |width| overlap.
  6111. @<Build Horiz...@>=
  6112. for hi := 1 to rules_on_page-1 do
  6113.  for hj := (hi+1) to rules_on_page do
  6114.   if (page_rules[horiz_map[hi]].vert > page_rules[horiz_map[hj]].vert) or
  6115.      ((page_rules[horiz_map[hi]].vert = page_rules[horiz_map[hj]].vert) and
  6116.       (page_rules[horiz_map[hi]].horiz > page_rules[horiz_map[hj]].horiz)) then
  6117.         begin ht:=horiz_map[hi]; horiz_map[hi]:=horiz_map[hj];
  6118.               horiz_map[hj]:=ht end;
  6119.  
  6120. @ Build map for vertical combination by sorting the rules into
  6121. increasing horizontal order and within the same horizontal line into
  6122. increasing vertical order. This allows for rule combination by
  6123. looking for |height| overlap.
  6124. @<Build Verti...@>=
  6125. for vi := 1 to rules_on_page-1 do
  6126.  for vj := (vi+1) to rules_on_page do
  6127.   if (page_rules[verti_map[vi]].horiz > page_rules[verti_map[vj]].horiz) or
  6128.      ((page_rules[verti_map[vi]].horiz = page_rules[verti_map[vj]].horiz) and
  6129.       (page_rules[verti_map[vi]].vert > page_rules[verti_map[vj]].vert)) then
  6130.         begin vt:=verti_map[vi]; verti_map[vi]:=verti_map[vj];
  6131.               verti_map[vj]:=vt end;
  6132.  
  6133. @ Traverse mapped rules looking for adjoining vertical rules. We actually
  6134. look for |height| overlap within |max_lap_rules|. Rules that have been
  6135. combined with other rules are set |covered := true|.
  6136. @<Traverse Verti...@>=
  6137. for vi:=1 to rules_on_page-1 do
  6138.  with page_rules[verti_map[vi]] do
  6139.   if not covered then
  6140.    for vj:= vi+1 to rules_on_page do
  6141.     if not page_rules[verti_map[vj]].covered then
  6142.      begin
  6143.      vt := vert - (page_rules[verti_map[vj]].vert -
  6144.                   page_rules[verti_map[vj]].height);
  6145.      if ((horiz = page_rules[verti_map[vj]].horiz) and
  6146.         (width = page_rules[verti_map[vj]].width) and
  6147.          (abs(vt) <= max_lap_rules)) then
  6148.        begin
  6149.         vert := page_rules[verti_map[vj]].vert;
  6150.         height := height + page_rules[verti_map[vj]].height - vt;
  6151.         page_rules[verti_map[vj]].covered := true;
  6152.        end;
  6153.      end;
  6154.  
  6155. @ Traverse mapped rules looking for adjoining horizontal rules. We actually
  6156. look for |width| overlap within |max_lap_rules|. Rules that have been
  6157. combined with other rules are set |covered := true|.
  6158. @<Traverse Horiz...@>=
  6159. for hi:=1 to rules_on_page-1 do
  6160.  with page_rules[horiz_map[hi]] do
  6161.   if not covered then
  6162.    for hj:= hi+1 to rules_on_page do
  6163.     if not page_rules[horiz_map[hj]].covered then
  6164.      begin
  6165.      ht := (horiz+width) - page_rules[horiz_map[hj]].horiz;
  6166.      if ((vert = page_rules[horiz_map[hj]].vert) and
  6167.         (height = page_rules[horiz_map[hj]].height) and
  6168.          (abs(ht) <= max_lap_rules)) then
  6169.        begin
  6170.         width := width + page_rules[horiz_map[hj]].width - ht;
  6171.         page_rules[horiz_map[hj]].covered := true;
  6172.        end;
  6173.      end;
  6174.  
  6175. @ @<Declare the function called |special_cases|@>=
  6176. function special_cases(@!o:eight_bits;@!p,@!a:integer):boolean;
  6177. label change_font,move_down,done,9998;
  6178. var q:integer; {parameter of the current command}
  6179. @!k:integer; {loop index}
  6180. @!bad_char:boolean; {has a non-ASCII character code appeared in this \\{xxx}?}
  6181. @!pure:boolean; {is the command error-free?}
  6182. @!vvv:integer; {|v|, rounded to the nearest pixel}
  6183. begin pure:=true;
  6184. case o of
  6185. @t\4@>@<Cases for vertical motion@>@;
  6186. @t\4@>@<Cases for fonts@>@;
  6187. four_cases(xxx1): @<Translate an |xxx| command and |goto done|@>;
  6188. pre: begin error('preamble command within a page!'); goto 9998;
  6189.   end;
  6190. @.preamble command within a page@>
  6191. post,post_post: begin error('postamble command within a page!'); goto 9998;
  6192. @.postamble command within a page@>
  6193.   end;
  6194. othercases begin error('undefined command ',o:1,'!');
  6195.   goto done;
  6196. @.undefined command@>
  6197.   end
  6198. endcases;
  6199. move_down: @<Finish a command that sets |v:=v+p|, then |goto done|@>;
  6200. change_font: @<Finish a command that changes the current font,
  6201.   then |goto done|@>;
  6202. 9998: pure:=false;
  6203. done: special_cases:=pure;
  6204. end;
  6205.  
  6206. @ If we meet a |bop|, something's gone amiss, because |do_page| should never
  6207. see one of these commands.
  6208.  
  6209. When |eop| is met, we have to cater for two possibilities:
  6210. \item{$\bullet$} It's the end of a page in the \.{DVI} file, in which case
  6211. we must output all the glyphs, rules and bitmaps that we've accumulated;
  6212. having done this, we reset |max_h_so_far| and |max_v_so_far| so that each
  6213. page's dimensions are considered under the same conditions.
  6214. \item{$\bullet$} It terminates a stored sequence of |dvi| bytes
  6215. corresponding to a character taken from a virtual font.
  6216.  
  6217. If |push| is met, we don't bleat about exceeding the maximum stack size
  6218. reported by \TeX\ if the command has been read from a virtual character
  6219. sequence.
  6220.  
  6221. @<Cases for commands |nop|, |bop|, \dots, |pop|@>=
  6222. nop: goto done;
  6223. bop: begin error('bop occurred before eop!'); goto 9998;
  6224. @.bop occurred before eop@>
  6225.   end;
  6226. eop:if vf_take<0 then
  6227.     begin
  6228.       if s<>0 then error('stack not empty at end of page (level ',
  6229.             s:1,')!');
  6230. @:Error: stack not empty}{\quad\.{stack not empty...}@>
  6231.       if max_h<>max_h_saved then
  6232.         warning('Page wider than TeX reported by ',
  6233.                      (max_h-max_h_saved)/65536.0:1:4,'pt');
  6234. @:Warning: Page wider than TeX reported}{\quad\.{Page wider than }\TeX\.{...}@>
  6235.       if max_v<>max_v_saved then
  6236.         warning('Page deeper than TeX reported by ',
  6237.                      (max_v-max_v_saved)/65536.0:1:4,'pt');
  6238. @:Warning: Page deeper than TeX reported}{\quad\.{Page deeper than }\TeX\.{...}@>
  6239.       @<Move to top of form@>;@/
  6240.       @<Combine and...@>@;
  6241.       do_page:=true;@/
  6242.       @<Output contents of page@>;@/
  6243.       write(ln3_file,LF);               {LF after page}
  6244.       new_orient:=orientation;
  6245.       @<Change page orientation@>;      {reset, if necessary}
  6246.       max_h:=max_h_saved; max_h_so_far:=max_h;
  6247.       max_v:=max_v_saved; max_v_so_far:=max_v;
  6248.       top_of_page:=true;
  6249.       goto 9999;
  6250.     end else
  6251.     begin
  6252.       do_page:=true;
  6253.       goto 9999
  6254.     end;
  6255. push: begin
  6256.   if s=max_s_so_far then
  6257.     begin max_s_so_far:=s+1;
  6258.     if (s=max_s) and (vf_take<0) then
  6259.       error('push deeper than claimed in postamble!');
  6260. @:Error: push deeper than claimed}{\quad\.{push deeper than claimed...}@>
  6261.     if s=stack_size then
  6262.       begin capacity_exceeded('stack size=',stack_size:1);
  6263. @:capacity exceeded stack size}{\quad\.{stack size}@>
  6264.         goto 9998;
  6265.       end;
  6266.     end;
  6267.   hstack[s]:=h; vstack[s]:=v; wstack[s]:=w;
  6268.   xstack[s]:=x; ystack[s]:=y; zstack[s]:=z;
  6269.   hhstack[s]:=hh; vvstack[s]:=vv; incr(s); goto done;
  6270.   end;
  6271. pop: begin
  6272.   if s=0 then error('pop illegal at level zero')
  6273. @:Error: pop illegal at level zero}{\quad\.{pop illegal at level zero}@>
  6274.     else  begin decr(s);
  6275.     if (vv <> vvstack[s]) and (vvstack[s]>=0) then finish_seg;
  6276.     {different row of pixels}
  6277.     hh:=hhstack[s]; vv:=vvstack[s];
  6278.     h:=hstack[s]; v:=vstack[s]; w:=wstack[s];
  6279.     x:=xstack[s]; y:=ystack[s]; z:=zstack[s];
  6280.     end;
  6281.   goto done;
  6282.   end;
  6283.  
  6284. @ Rounding to the nearest pixel is best done in the manner shown here, so as
  6285. to be inoffensive to the eye: When the horizontal motion is small, like a
  6286. kern, |hh| changes by rounding the kern; but when the motion is large, |hh|
  6287. changes by rounding the true position |h| so that accumulated rounding errors
  6288. disappear. We allow a larger space in the negative direction than in
  6289. the positive one, because \TeX\ makes comparatively
  6290. large backspaces when it positions accents.
  6291.  
  6292. @d out_space==if (p>=font_space[cur_font])or(p<=-4*font_space[cur_font]) then
  6293.     hh:=pixel_round(h+p)
  6294.   else hh:=hh+pixel_round(p);
  6295.   q:=p; goto move_right
  6296.  
  6297. @<Cases for horizontal motion@>=
  6298. four_cases(right1):begin out_space @+end;
  6299. w0,four_cases(w1):begin w:=p; out_space @+end;
  6300. x0,four_cases(x1):begin x:=p; out_space @+end;
  6301.  
  6302. @ Vertical motion is done similarly, but with the threshold between
  6303. ``small'' and ``large'' increased by a factor of five. The idea is to make
  6304. fractions like ``$1\over2$'' round consistently, but to absorb accumulated
  6305. rounding errors in the baseline-skip moves.
  6306.  
  6307. @d out_vmove==if abs(p)>=5*font_space[cur_font] then vv:=pixel_round(v+p)
  6308.   else vv:=vv+pixel_round(p);
  6309.   finish_seg;
  6310.   goto move_down
  6311.  
  6312. @<Cases for vertical motion@>=
  6313. four_cases(down1):begin out_vmove @+end;
  6314. y0,four_cases(y1):begin y:=p; out_vmove @+end;
  6315. z0,four_cases(z1):begin z:=p; out_vmove @+end;
  6316.  
  6317. @ @<Cases for fonts@>=
  6318. sixty_four_cases(fnt_num_0): goto change_font;
  6319. four_cases(fnt1): goto change_font;
  6320. four_cases(fnt_def1): begin define_font(p); goto done; end;
  6321.  
  6322. @ We need a certain amount of extra space in order to store the details of
  6323. \.{\BS special} commands as we work on them.
  6324.  
  6325. @<Glob...@>=
  6326. @!spec_par:packed array [1..max_special] of ASCII_code; {used to hold a
  6327. \.{\BS special} command}
  6328. @!spec_len: integer; {the space-stripped length of a \.{\BS special} command}
  6329. @!spec_ptr: integer; {pointer while decoding a \.{\BS special} command}
  6330. @!spec_cmd: str_number; {identifies a command string in a \.{\BS special}}
  6331. @!spec_unsupported : boolean;
  6332. @!spec_ch : ASCII_code;
  6333. @!point : array [1..max_points] of point_type;
  6334.  
  6335. @ This data structure records the co\"ordinates of a point generated by the
  6336. \.{defpoint} \.{\BS special}.  Such co\"ordinates are recorded in LN03
  6337. dimensions.
  6338.  
  6339. @<Types...@>=
  6340. @!point_type = packed record
  6341.  @!hh,@!vv : integer;
  6342.  @!present : boolean;
  6343. end;
  6344.  
  6345. @ Let's preset that which we've just defined\dots
  6346.  
  6347. @<Preset...@>=
  6348. for k:=1 to max_points do point[k].present:=false;
  6349.  
  6350. @ |xxx| commands in the DVI file arise from \.{\\special} commands in the
  6351. {\TeX} source file. \.{\\special} commands may be used for a variety of
  6352. things, so we must decode them further here.
  6353.  
  6354. We support two sorts of \.{\\special} name. The first derives from the
  6355. original (pre-V3) of this driver, of which there is but one example,
  6356. `\.{\\special\{SX\ file-name\}}'. The second sort takes the form:\par
  6357. \leavevmode\hbox{`\.{\\special\{LN03:}\meta{function-name}[\.\ %
  6358. \meta{other-parameters}[\.\ \dots]]\.\}'},\par
  6359. \noindent as in Flavio Rose's original. The \meta{function name}s
  6360. \.{landscape} and \.{portrait} can appear with or without the \.{LN03:}
  6361. prefix We support both versions, with the addition (over early versions of
  6362. this driver) that command decoding is case-insensitive.
  6363.  
  6364. This module does various sanity checks on the \.{\BS special} command's
  6365. parameter, ending up with its characters in |spec_par|, the count of characters
  6366. in |spec_len|, and |spec_ptr| pointing to the first character not yet `looked
  6367. at'.
  6368.  
  6369. The module (if successful) calls one of the subsidiary routines
  6370. |Copy_sixel_file|, |define_point|, |connect_points| or |reset_points|, actually
  6371. to implement the \.{\BS special} command before going to |done|.
  6372.  
  6373. @<Translate an |xxx| command and |goto done|@>=
  6374. begin bad_char:=false;
  6375. if p<0 then error('\special string of negative length!');
  6376. @:Error: special string of negative length}{\quad\.{\\special string of negative length}@>
  6377. if p>max_special then
  6378. begin error('\special string too long!');
  6379. @:Error: special string too long}{\quad\.{\\special string too long}@>
  6380.   p:=max_special
  6381. end;
  6382. for k:=1 to p do
  6383.   begin q:=get_byte;
  6384.   if (q<" ")or(q>"~") then bad_char:=true else
  6385.   spec_par[k]:=q;
  6386.   end;
  6387.  
  6388. {intialize the \.{\\special} decoding routines}
  6389. spec_len:=p; spec_ptr:=1;
  6390.  
  6391. {\dots\ and start decoding!!!}
  6392. @<Find start of \.{\\special}'s string(s)@>;
  6393. if not bad_char then begin
  6394. @<Match first command in \.{\\special}@>;
  6395. @<Match the \meta{function name} in a \.{\\special}@>;
  6396. end;
  6397. if bad_char then error('non-ASCII character in \special command!')
  6398. @:Error: non-ASCII character in special}{\quad\.{non-ASCII character in \\special}@>
  6399. else if spec_unsupported
  6400. then spec_error('Unsupported \special ');
  6401. @:Error: Unsupported special}{\quad\.{Unsupported \\special}@>
  6402. spec_unsupported:=false;
  6403. goto done;
  6404. end
  6405.  
  6406. @ If there's any space characters at the start of the \.{\\special}
  6407. string, we can safely skip past them\dots
  6408.  
  6409. @<Find start of \.{\\special}'s string(s)@>=
  6410. if rd_nb_char(spec_ch) then
  6411.   begin
  6412.   error('Null \special argument!');
  6413. @:Error: Null special argument}{\quad\.{Null \\special argument}@>
  6414.   goto done;
  6415.   end;
  6416. bksp_char
  6417.  
  6418. @ We have a very limited choice of commands that may be given without being
  6419. preceded by the device specifier `\.{ln03:}'\dots
  6420.  
  6421. @<Match first command in \.{\\special}@>=
  6422. spec_cmd := find_match;
  6423. case spec_cmd of
  6424.   spec_sx: spec_cmd := spec_six;        {Substitute |'ln03:sixel'| for |'sx'|}
  6425.   spec_ln: spec_cmd := find_match;      {Find string following |'ln03:'|}
  6426.   spec_land, spec_port: do_nothing;     {Recognize again in next section}
  6427.   othercases
  6428.     spec_cmd:=0;    {Command \&{must} start with |'sx '| or |'ln03:'|!}
  6429. endcases
  6430.  
  6431. @ The majority of \.{\\special} \meta{function name}s will be recognized
  6432. here\dots
  6433.  
  6434. @<Match the \meta{function name} in a \.{\\special}@>=
  6435. case spec_cmd of
  6436.   spec_land: new_orient:=landscape; {|'landscape'| or |'ln03:landscape'|}
  6437.   spec_port: new_orient:=portrait;  {|'portrait'| or |'ln03:portrait'|}
  6438.   spec_six, spec_plt:           {|'ln03:sixel'| or |'ln03:plotfile'|}
  6439.         include_sixel_dump;
  6440.   spec_set:                     {|'ln03:defpoint'|}
  6441.         define_point;
  6442.   spec_con:                     {|'ln03:connect'|}
  6443.         connect_points;
  6444.   spec_rst:                     {|'ln03:resetpoints'|}
  6445.         reset_points;
  6446.   othercases
  6447.     spec_unsupported:=true
  6448. endcases
  6449.  
  6450.  
  6451. @ @<Translate a |set_char|...@>=
  6452. goto fin_set
  6453.  
  6454. @ @<Finish a command that either sets or puts a character...@>=
  6455. if p<0 then p:=255-((-1-p) mod 256)
  6456. else if p>=256 then p:=p mod 256; {width computation for oriental fonts}
  6457. @^oriental characters@>@^Chinese characters@>@^Japanese characters@>
  6458. if (p<font_bc[cur_font])or(p>font_ec[cur_font]) then q:=invalid_width
  6459. else q:=cur_font_glyph(p).width;
  6460. if q=invalid_width then
  6461.   begin error('character ',p:1,' invalid in font ');
  6462. @.character $c$ invalid...@>
  6463.   print_font(cur_font);
  6464.   if cur_font<>nf then print('!'); {font |nf| has `\.!' in its name}
  6465.   end;
  6466.   if font_type[cur_font]=missing then
  6467.     @<Substitute a solid rule for character from missing font@>
  6468.   else
  6469.     ord_text(p);
  6470. if o>=put1 then goto done;
  6471. if q=invalid_width then q:=0
  6472. else hh:=hh+cur_font_glyph(p).pixel_width;
  6473. goto move_right
  6474.  
  6475. @ @<Finish a command that either sets or puts a rule...@>=
  6476. q:=signed_quad;
  6477. if (p>0)and(q>0) then @<Collect Rules...@>@;
  6478. if o=put_rule then goto done;
  6479. hh:=hh+rule_pixels(q); goto move_right
  6480.  
  6481. @ A sequence of consecutive rules, or consecutive characters in a fixed-width
  6482. font whose width is not an integer number of pixels, can cause |hh| to drift
  6483. far away from a correctly rounded value. \.{DVItoLN03} ensures that the
  6484. amount of drift will never exceed |max_drift| pixels.
  6485.  
  6486. Since \.{DVItype} is intended to diagnose strange errors, it checks
  6487. carefully to make sure that |h| and |v| do not get out of range.
  6488. Normal \.{DVI}-reading programs need not do this, but \.{DVItoLN03} makes some
  6489. of these same checks and reports characters which are ``set'' outside the page
  6490. (as reported by \TeX\ itself).
  6491.  
  6492. No warning is given when |max_h_so_far| exceeds |max_h| by less than~100,
  6493. since 100 units is invisibly small; it's approximately the wavelength of
  6494. visible light, in the case of \TeX\ output. Rounding errors can be expected
  6495. to make |h| and |v| slightly more than |max_h| and |max_v|, every once in
  6496. a~while; hence small discrepancies are not cause for alarm.
  6497.  
  6498. @d infinity==@'17777777777 {$\infty$ (approximately)}
  6499. @d max_drift=2 {we insist that abs|(hh-pixel_round(h))<=max_drift|}
  6500.  
  6501. @<Finish a command that sets |h:=h+q|, then |goto done|@>=
  6502. if (h>0)and(q>0) then if h>infinity-q then
  6503.   begin error('arithmetic overflow! parameter changed from ',@|
  6504. @.arithmetic overflow...@>
  6505.     q:1,' to ',infinity-h:1);
  6506.   q:=infinity-h;
  6507.   end;
  6508. if (h<0)and(q<0) then if -h>q+infinity then
  6509.   begin error('arithmetic overflow! parameter changed from ',@|
  6510.     q:1,@| ' to ',@|(-h)-infinity:1);
  6511.   q:=(-h)-infinity;
  6512.   end;
  6513. @<Record current position of any text output@>;
  6514. hhh:=pixel_round(h+q);
  6515. if abs(hhh-hh)>max_drift then
  6516.   if hhh>hh then hh:=hhh-max_drift
  6517.   else hh:=hhh+max_drift;
  6518. h:=h+q;
  6519. if abs(h)>max_h_so_far then
  6520.   begin if abs(h)>max_h+h_fuzz then
  6521.     max_h:=abs(h);
  6522.   max_h_so_far:=abs(h);
  6523.   end;
  6524. goto done
  6525.  
  6526. @ If the |cur_font| wasn't available for downloading to the LN03, instead of
  6527. imaging the glyph we output a solid rule of the dimensions given by the
  6528. \.{TFM} file (on the proviso that the \.{DVI} file couldn't have been
  6529. generated by \TeX\ if the font was totally unknown).
  6530.  
  6531. @<Substitute a solid rule for character from missing font@>=
  6532.   with cur_font_glyph(p) do
  6533.     Collect_rule(hh,vv+pixel_depth,pixel_width,pixel_height+pixel_depth)
  6534.  
  6535. @ Procedure |do_page| gets to label |move_right| to record the rightmost
  6536. excursion of any character that's just been set: however, with virtual
  6537. fonts, we can end up with |cur_seg=nil|.
  6538.  
  6539. @<Record current position of any text output@>=
  6540. if ((o<set_char_0+128) or
  6541.   ((o>=set1) and (o<set1+4)) or
  6542.   ((o>=put1) and (o<put1+4))) and
  6543.   (cur_seg<>nil) then
  6544.     cur_seg^.xe:=x_pos;
  6545.  
  6546. @ @<Finish a command that sets |v:=v+p|, then |goto done|@>=
  6547. if (v>0)and(p>0) then if v>infinity-p then
  6548.   begin error('arithmetic overflow! parameter changed from ',
  6549. @.arithmetic overflow...@>
  6550.     p:1,' to ',infinity-v:1);
  6551.   p:=infinity-v;
  6552.   end;
  6553. if (v<0)and(p<0) then if -v>p+infinity then
  6554.   begin error('arithmetic overflow! parameter changed from ',
  6555.     p:1, ' to ',(-v)-infinity:1);
  6556.   p:=(-v)-infinity;
  6557.   end;
  6558. vvv:=pixel_round(v+p);
  6559. if abs(vvv-vv)>max_drift then
  6560.   if vvv>vv then vv:=vvv-max_drift
  6561.   else vv:=vvv+max_drift;
  6562. v:=v+p;
  6563. if abs(v)>max_v_so_far then
  6564.   begin if abs(v)>max_v+v_fuzz then
  6565.     max_v:=abs(v);
  6566.   max_v_so_far:=abs(v);
  6567.   end;
  6568. goto done
  6569.  
  6570. @ @<Finish a command that changes the current font...@>=
  6571. font_num[nf]:=p; cur_font:=0;
  6572. while font_num[cur_font]<>p do incr(cur_font);
  6573. if cur_font=nf then
  6574.   error('reference to an unloaded font');
  6575. @:Error: reference to unloaded font}{\quad\.{reference to unloaded font}@>
  6576. cur_font:=font_map[cur_font]; cur_base:=glyph_base[cur_font];
  6577. finish_seg;
  6578. goto done
  6579.  
  6580. @* Font usage pass.  Whilst gathering the font usage statistics, each |bop|
  6581. command will cause various variables to be updated which the ``real'' pass
  6582. through the file would not expect to have changed.  Therefore, we copy the
  6583. state of these variables after recognising the start of the first page
  6584. requested, and restore them (and the file position), before commencing the
  6585. true output pass.  The Boolean |scanning| inhibits output to the \.{TYP} file
  6586. during this phase.
  6587.  
  6588. @<Globals...@>=
  6589. @!start_page_count:integer;
  6590. @!scanning:boolean;
  6591.  
  6592. @ @<Preset...@>=
  6593. scanning:=false;
  6594.  
  6595. @ Having found the first page which is eligible for translation to LN03
  6596. format, we skip rapidly through the remainder of the file, noting whether any
  6597. particular character of each font is actually used in the output.  The file is
  6598. then further read until the postamble is reached, and the character usage
  6599. information used to generate the appropriate down-line loadable font(s) for
  6600. the LN03.
  6601. @^down-line loading of fonts@>
  6602.  
  6603. @<Gather font usage statistics@>=
  6604. begin
  6605.   monitor('Gathering font usage statistics...');
  6606. @.Gathering font usage statistics@>
  6607.   start_loc:=old_backpointer; { Remember where the first page started }
  6608.   start_page_count:=page_count-1; { Preserve, since |bop|s will change this }
  6609.   scanning:=true;@/
  6610.   skip_pages; { Note usage of any required characters }
  6611.   scanning:=false; {We've completed the scanning phase}
  6612.   if signed_quad<>old_backpointer then
  6613.     error('backpointer in byte ',cur_loc-4:1,
  6614.         ' should be ',old_backpointer:1,'!');
  6615. @:Error: backpointer in byte should be}{\quad\.{backpointer...should be...}@>
  6616.   substitution_pass:=true;      {Prevents spurious ``font already defined''s}
  6617.   read_postamble;@/
  6618.   @<Assign font/char mapping@>;@/
  6619.   @<Download font files@>;@/
  6620.   @<Restore pointers after font usage pass@>;@/
  6621.   in_postamble:=false;
  6622. end
  6623.  
  6624. @ After we've finished the font usage pass, we must restore all the various
  6625. pointers into the |dvi_file| to their state preceding the pass, so that the
  6626. main program can just carry on with the actual setting of the glyphs.
  6627.  
  6628. @<Restore pointers after font usage pass@>=
  6629. old_backpointer:=start_loc;     {for when the next |bop| is met}
  6630. move_to_byte(start_loc+1);      {Go back to just after |bop| of starting page}
  6631. for k:=0 to 9 do count[k]:=signed_quad; {fill in page number array}
  6632. p:=signed_quad; {Swallow the |old_backpointer|}
  6633. page_count:=start_page_count
  6634.  
  6635. @*Map characters to LN03 fonts. At this point, we know which characters have
  6636. actually been invoked in each of the \TeX\ fonts.  Since the LN03 has
  6637. limited font memory, as many characters as practicable are packed into a
  6638. 188-character LN03 font; these characters will typically be taken from at
  6639. least two \TeX\ fonts.
  6640. @^LN03 laser printer@>
  6641.  
  6642. We therefore require to know, for each \TeX\ character, to which LN03 font it has
  6643. been allocated, and what character value it occupies in that font.  When
  6644. characters are actually being typeset, only ten LN03 fonts are directly
  6645. accesible at any one time (corresponding to the Select Graphic Rendition
  6646. parameters 10..19); if the character to be typeset is allocated to a different
  6647. LN03 font, a suitable escape sequence will need to be output to change the
  6648. font which the LN03 is accessing.
  6649.  
  6650. The following declarations accomplish this mapping$\ldots$
  6651.  
  6652. @d left_first="!"       {These are the ``printable'' character ranges}
  6653. @d left_last="~"        {which may appear in an LN03 font}
  6654. @d right_first=left_first+128
  6655. @d right_last=left_last+128
  6656.  
  6657. @ For any particular \TeX\ font, |the_txf|, we count in
  6658. |font_occupancy[the_txf]| the number of characters invoked from that font.
  6659. To save all that indexing of arrays for this, the counting actually takes
  6660. place in |usage|.  The array |txf_to_lnf| provides the number of the LN03
  6661. internal font into which |the_txf| has been mapped, whilst |txf_ord| is a
  6662. list of |the_txf|s in the order in which they were mapped (largest first).
  6663. For each of the LN03's internal fonts, |lnf|, the array element
  6664. |last_lnf[lnf]| contains the character code of the last character used
  6665. therein to map a character. |ch_widths| is an array which records the actual
  6666. width, in pixels, of each character created within an LN03 font.
  6667.  
  6668. The scalar variables used in this section include: (1) |txf_size| which
  6669. records how many characters are used in the next largest \TeX\ font
  6670. remaining; (2)  |lnf_spare| records the number of unallocated glyphs
  6671. remaining in the LN03 font-file currently being filled; (3) |total_rasters|
  6672. counts the number of bytes of character rasters downloaded (this is reported
  6673. on the terminal and the \.{TYP} log file, and may be important to
  6674. installations without additional RAM font memory on the LN03); (4)
  6675. |pxl_ident| reminds us whether |the_txf| exists as a packed or unpacked
  6676. pixel file.
  6677.  
  6678. @<Globals in...@>=
  6679. @!font_occupancy:packed array [0..max_fonts] of 0..256; {Number of characters used}
  6680. @!usage:-1..256;
  6681. @!next_lnf:0..max_lnfonts+1;    {Next LN03 font index to be allocated}
  6682. @!next_txf:0..max_fonts;        {Next \TeX\ font to be mapped}
  6683. @!txf_size:-1..256;             {Occupancy of largest remaining \TeX\ font}
  6684. @!txf_to_lnf:packed array [0..max_fonts] of -1..max_lnfonts; {Maps \TeX\ font to LN03 font}
  6685. @!txf_ord:packed array [0..max_fonts] of -1..max_fonts; {Contains \TeX\ font numbers in
  6686. order of mapping}
  6687. @!the_txf:0..max_fonts; {TeX font currently being mapped}
  6688. @!lnf_spare:integer;    {``Spare'' room in current LN03 font}
  6689. @!last_lnf:packed array [0..max_lnfonts] of left_first..right_last;
  6690. {``Last'' character allocated in an LN03 font}
  6691. @!total_rasters:integer;        {Total size of down-loaded fonts}
  6692. @!ch_widths:array [0..max_lnfonts,left_first..right_last] of integer;
  6693. @!pxl_ident:pixel_types;
  6694.  
  6695. @ At present, none of the LN03 fonts are `in use'; neither have any of them
  6696. been mapped to |SGR| (Select Graphic Rendition) codes.
  6697.  
  6698. We also clear out the variable in which we accumulate the count of all bytes
  6699. transferred as character rasters.
  6700.  
  6701. @<Set initial...@>=
  6702. for m:=0 to max_lnfonts do font_in_use[m]:=-1;
  6703. for m:=0 to max_SGR do who_uses[m]:=-1;
  6704. total_rasters:=0;
  6705.  
  6706. @ We now perform the actual mapping.  Each \TeX\ font is considered in turn,
  6707. starting with the largest (most densely occupied) as yet unallocated.  The
  6708. characters of this font are mapped to the next available LN03 font, and the
  6709. remaining space in the LN03 font is allocated to the largest \TeX\ fonts which
  6710. will fit.  When no such \TeX\ font will fit in the remaining space, a new LN03
  6711. font is ``opened''.
  6712.  
  6713. Before we go ahead and map \TeX\ characters to physical ones, we have to
  6714. ensure that we're not still considering virtual fonts.
  6715.  
  6716. @<Assign font/char mapping@>=
  6717. @<Remap any virtual fonts@>;
  6718. @<Determine occupancy of each \TeX\ font@>;
  6719. next_lnf:=0; next_txf:=0;       {Order of fonts}
  6720. repeat
  6721.   lnf_spare:=left_last-left_first+1+right_last-right_first+1; {therefore =
  6722.   188}
  6723.   txf_size:=-1;         {We're searching for largest font}
  6724.   @<Find largest unallocated font@> ;
  6725.   if txf_size>0 then    {Assuming there are any unallocated}
  6726.   begin
  6727.     if next_lnf>max_lnfonts then capacity_exceeded('too many LN03 fonts');
  6728. @:capacity exceeded too many LN03 fonts}{\quad\.{too many LN03 fonts}@>
  6729.     m:=left_first-1;            {We'll start with the ``!'' character}
  6730.     @<Map characters in \TeX\ font to allocated LN03 font@>;
  6731.     @<Fit further \TeX\ fonts into LN03 font file@>;
  6732.     incr(next_lnf)
  6733.   end;  {Fitted largest \TeX font, and some more, into an LN03 font}
  6734. until txf_size<=0;      {Repeat until all fonts mapped}
  6735.  
  6736. @ We firstly require to know how many different characters have been invoked
  6737. from each of the \TeX\ fonts used in the document.  The following section of
  6738. code performs this feat.
  6739.  
  6740.  
  6741. To speed things up, we avoid the double indexing of |glyphs| that would
  6742. occur if we used |glyph_map|; these definitions help.
  6743.  
  6744. @d first_glyph_index(#)==glyph_base[#]+font_bc[#]
  6745. @d last_glyph_index(#)==glyph_base[#]+font_ec[#]
  6746. @<Determine occupancy of each \TeX\ font@>=
  6747. for m:=0 to nf-1 do     {Determine how occupied each \TeX\ font is}
  6748. begin usage:=0;
  6749.   for n:=first_glyph_index(m) to last_glyph_index(m) do
  6750.     if glyphs[n].loaded=wanted then incr(usage);
  6751.   font_occupancy[m]:=usage;
  6752.   txf_ord[m]:=-1;       {Font unallocated}
  6753.   txf_to_lnf[m]:=-1
  6754. end
  6755.  
  6756. @ Because we want to avoid having a single \TeX\ font overlapping two separate
  6757. fonts in the LN03 (which would require more control sequences to be output),
  6758. we prefer to have a large font starting off a new |ln_font|.  This can be
  6759. achieved by finding the largest font that has not yet been allocated a
  6760. ``home''.
  6761.  
  6762. (After mapping this font, we can use up the remaining unallocated characters of
  6763. the |ln_font| by putting smaller \TeX\ fonts into the same |ln_font|.)
  6764.  
  6765. It might prove embarrassing to attempt to map a virtual font, so we only
  6766. load fonts that are marked as |wanted|; this excludes |virtual| fonts, and
  6767. also those referenced from such a font from which all the glyphs are
  6768. |unused|.
  6769.  
  6770. @<Find largest unallocated font@>=
  6771. for n:=0 to nf-1 do
  6772. if (font_type[n]=wanted) and (font_occupancy[n]>txf_size) then
  6773.   begin txf_size:=font_occupancy[n];
  6774.     the_txf:=n
  6775.   end
  6776. @ Having fitted a ``fullish'' \TeX\ font into a fresh LN03 font, we now
  6777. attempt to fill up the space remaining in the latter by squeezing in as many
  6778. smaller fonts as will fit.  |lnf_spare| has been set to indicate how many
  6779. unallocated glyphs remain in the LN03 font, so we find the largest font whose
  6780. occupancy is not greater than this amount, and map it in; this process is
  6781. repeated until there is no \TeX\ font remaining that will fit in the
  6782. |lnf_spare| space remaining, at which point a new LN03 font is started (if
  6783. necessary).
  6784.  
  6785. @<Fit further \TeX\ fonts into LN03 font file@>=
  6786. while lnf_spare>0 do    {Now find any \TeX\ font that fits in remainder}
  6787. begin  usage:=-1;       {Performs same function as |txf_size| above}
  6788.   for n:=0 to nf-1 do   {Find largest remaining that fits}
  6789.     if (font_type[n]=wanted) and {provided it's physical, unmapped and}
  6790.       (font_occupancy[n]>usage) and {it's more densely used}
  6791.       (font_occupancy[n]<=lnf_spare) then {and it fits in space available}
  6792.     begin usage:=font_occupancy[n];
  6793.       the_txf:=n
  6794.     end;@t\2@>
  6795.   if usage > 0 then             {There's one that fits!}
  6796.   begin
  6797.     m:=last_lnf[next_lnf];      {Last character allocated in LN03 font}
  6798.     @<Map characters in \TeX\...@>;
  6799.   end  else
  6800.     lnf_spare:=0;       {Nothing else fits; force new LN03 font}
  6801. end  { while spare space remains in LN03 font}
  6802.  
  6803.  
  6804. @ The following code maps the characters in the current (|the_txf|) \TeX\ font
  6805. to character positions in the current (|next_lnf|) LN03 font.  No attempt is
  6806. made to make the mapped characters intelligible if the \.{LN3} file is
  6807. printed; this would be a ``nice'' revision of the program, but well nigh
  6808. impossible!
  6809.  
  6810. @<Map characters in \TeX\...@>=
  6811. txf_to_lnf[the_txf]:=next_lnf;  {Map \TeX\ font to LN03 font}
  6812. txf_ord[next_txf]:=the_txf;     {Save \TeX\ font numbers, largest first}
  6813. font_type[the_txf]:=yes;        {Avoid considering this font again!}
  6814. incr(next_txf);
  6815. for k:=first_glyph_index(the_txf) to last_glyph_index(the_txf) do
  6816.   with glyphs[k] do
  6817.   if loaded=wanted then {Work through the characters of the \TeX\ font}
  6818.   begin
  6819.     @<Put LN03 code for this \TeX\ character into |m|@> ;
  6820.     decr(lnf_spare);    {Count one less free}
  6821.     char_code:=m;
  6822.   end;
  6823. last_lnf[next_lnf]:=m   {Note ``last'' character allocated in LN03 font}
  6824.  
  6825.  
  6826. @ As we allocate characters from |the_txf| to the LN03 font |next_lnf|, we
  6827. have to ensure that the character codes used are those of \&{printing}
  6828. characters on the LN03.  The LN03 uses the customary \.{C0} and \.{C1} control
  6829. code sets of the ISO/ASCII standards, and so the only printable characters are
  6830. `\.\SP' to `\.\TL' (|left_first..left_last|) and the corresponding
  6831. positions with the eighth bit set (|right_first..right_last|).  This coding
  6832. scheme has ``holes'' in it, so we need to make appropriate adjustments.
  6833.  
  6834. Furthermore, if we permit a large font (with more than 188 characters used) to
  6835. span across into a second LN03 internal font, we have to make the transition
  6836. to the beginning of the second font, noting the false ``last character used''
  6837. and revising |lnf_spare| to reflect the new (unused) font.
  6838.  
  6839. @<Put LN03 code for this \TeX\ character into |m|@>=
  6840. if m=right_last then  {Start another LN03 font for spanning \TeX\ font}
  6841.   begin last_lnf[next_lnf]:=right_last;
  6842.     lnf_spare:=left_last-left_first+1+right_last-right_first+1;
  6843.     m:=left_first;
  6844.     incr(next_lnf)
  6845.   end
  6846. else
  6847.   if m=left_last then m:=right_first {make transition to GR set in font}
  6848.   else incr(m); {Next free character in LN03 font}
  6849.  
  6850. @*Generating LN03 font for downloading.
  6851. The following code generates the content of an LN03 font file, in memory,
  6852. and then calls |add_txf_to_lnf| to put the rasters into this file.
  6853. The file type extension is \.{.LN3}, and this file is always opened in the
  6854. user's default directory.
  6855.  
  6856.  
  6857. The next section of code actually generates the \.{LN3} file, starting with
  6858. its ``set up'' information.  The program continues by creating and downloading
  6859. each necessary LN03 font, by successive calls of the function
  6860. |add_txf_to_lnf|, which returns as result the number of bytes downloaded in
  6861. the rasters of the characters contained therein; this is added to
  6862. |total_rasters| so that DVItoLN03 can report the size of all the fonts loaded.
  6863.  
  6864. The first (or only) LN03 font file downloaded is introduced by the |DCS|
  6865. (Device Control Sequence) |'0;1;0y'|; these three parameters indicate,
  6866. respectively, that the font file is in Digital font file format, is \&{not} to
  6867. print a summary sheet, and is to replace all previously downloaded fonts.
  6868. Successive LN03 fonts are separated from the previous ones by a simple |','|
  6869. character; whilst the end of the font download is marked by the |ST| (String
  6870. Terminator) character --- this is preceded by a textual comment.
  6871.  
  6872. @<Download font files@>=
  6873. @<Generate \.{LN3} file header@>;
  6874. total_rasters:=0;
  6875. if next_txf > 0 then            {Unlikely that no characters were used, but...}
  6876. begin
  6877.   write_ln(ln3_file,dcs,'0;1;0y');      {Replace all fonts with these}
  6878.   for m:=0 to next_txf-1 do     {Cycle through the ``used'' \TeX\ fonts}
  6879.   begin k:=txf_ord[m];  {Get \TeX\ font number corresponding to next largest font}
  6880.     name_pxl_file(k,file_name,pxl_ident);
  6881.     if (m>0) then
  6882.       @<Separate LN03 internal fonts@> ;
  6883.     @<Copy glyphs to LN03 font; report size of rasters@>
  6884.   end;
  6885.   write_ln(ln3_file,';DVItoLN03 font load',st); {End of all fonts}
  6886. end;
  6887. print_ln('Total of ',total_rasters:1,' bytes of rasters downloaded');
  6888. write_ln(term_out,'Total of ',total_rasters:1,
  6889.                         ' bytes of rasters downloaded',crlf);
  6890. @:Total of n bytes of rasters}{\.{Total of $n$ bytes of rasters...}@>
  6891. @<Complete mapping of virtual characters@>; {point to the real ones}
  6892. {Set page limits}
  6893. write(ln3_file,csi,x_min:1,';',page_wid:1,'s',csi,y_min:1,';',page_len:1,'r');
  6894. @<Designate first 10 fonts to SGRs |10..19|@>@;
  6895.  
  6896. @ We start by writing the LN03 directives to the front of the file.  Since the
  6897. introduction of the LN03-Plus, this has been made more complicated, because
  6898. the sequence |esc, 'c'| is meaningful (and \&{not} as |RIS|!) when the
  6899. LN03-Plus is in the Tektronix emulation mode.  Therefore, we start off by
  6900. outputting the sequence |esc, '[!p'| which is DEC's private ``soft terminal
  6901. reset'' (|DSCSTR|).  (Note that this sequence uses the ``long-winded'' way of
  6902. specifying |csi|; this is because the LN03-plus ignores the eigth bit when it
  6903. is emulating a Tektronix terminal.)  However, this won't necessarily reset an
  6904. `ordinary' LN03, so we output the ISO-standard |RIS| (Reset Initial State
  6905. $\equiv$ |esc,'c'|) sequence as well.
  6906.  
  6907. There then follow a number of control sequences which set up further
  6908. characteristics of the LN03: (1) |'?27h'| is a DEC private control sequence,
  6909. |DECPSP|, which selects proportional spacing mode, whereby the printer moves
  6910. forward by the recorded width of the character glyph rather than by some fixed
  6911. amount after printing; (2) |'11h'| \&{set}s the |PUM| (Positioning Unit Mode),
  6912. when set, this mode indicates that ``cursor movement'' sequences work in units
  6913. of either decipoints or pixels, which of these is determined by (3) |'7 I'|
  6914. which sets the |SSU| (Select Size Unit) to be in units of a single pixel; (4)
  6915. |'?52h'| is another DEC private sequence, \&{set}ting the |DECOPM| (Origin
  6916. Placement Mode) which places the origin for all measurement to the upper-left
  6917. corner of the physical page (the alternative is 0.25 inches from that point).
  6918. Finally, the sequence ending in |'t'| contains the |page_len| as a parameter,
  6919. and is another DEC private control sequence which sets the |DECLPP| (Lines per
  6920. Physical Page) to be the height of the printable area, in pixels.
  6921.  
  6922. @d DECSTR==esc+'[!p'
  6923. @d RIS==esc+'c'
  6924. @d DECPSP_on==csi+'?27h'
  6925. @d PUM_on==csi+'11h'
  6926. @d SSU_pixel==csi+'7 I'
  6927. @d DECOPM_on==csi+'?52h'
  6928.  
  6929. @<Generate \.{LN3} file header@>=
  6930. if orientation=landscape then
  6931.   write_ln(ln3_file,DECSTR,RIS,csi,PFS_landscape)
  6932. else
  6933.   write_ln(ln3_file,DECSTR,RIS,csi,PFS_portrait);
  6934. cur_orient:=orientation; new_orient:=orientation;
  6935. write_ln(ln3_file,DECPSP_on,PUM_on,SSU_pixel,DECOPM_on,csi,page_len:1,'t');
  6936. @<Select printing mode and feed tray on DEClaser printers@>
  6937.  
  6938. @ If we are starting the download of any except the first \TeX\ font, it may
  6939. be that the previous \TeX\ font was the last loaded into the current LN03
  6940. font, which means we will be starting a new LN03 font here.  Therefore we need
  6941. to separate the two LN03 fonts from each other: the comma (`\.,') character is
  6942. used by the LN03 for this purpose.
  6943.  
  6944. @<Separate LN03 internal fonts@>=
  6945. if (txf_to_lnf[k]<>txf_to_lnf[txf_ord[m-1]]) then
  6946.       write_ln(ln3_file,',')
  6947.  
  6948. @ The function |add_txf_to_lnf| performs all the work of creating an LN03 font
  6949. from the rasters in the font file.  The result returned by this function
  6950. gives the total number of bytes created in the LN03 font for the given \TeX\
  6951. file.
  6952.  
  6953. @<Copy glyphs to LN03 font; report size of rasters@>=
  6954. total_rasters:=total_rasters+
  6955.   add_txf_to_lnf(txf_to_lnf[k],k,file_name,pxl_ident)
  6956.  
  6957. @ We may have both packed and unpacked files available from which to take
  6958. pixel bitmaps, so we require some indication of what type of file is
  6959. actually available.
  6960.  
  6961. @<Types...@>=
  6962. @!pixel_types = (@!unavailable,@!packed_file,@!pixel_file);
  6963.  
  6964. @ The user has the option of using either pixel files (\.{.PXL}) and/or packed
  6965. pixel files (\.{.PK}) as the source of font raster information.  There is the
  6966. further option of the directories in which these files are held being either
  6967. ``flat'' (with all font files of one class in the same directory) or
  6968. ``nested'' (where separate sub-directories are provided for each different
  6969. magnification).
  6970.  
  6971. File naming conventions used are illustrated by the following examples, which
  6972. show the names under which \.{cmr10} will be found at magsteps 0--2:
  6973.  
  6974. \yskip\centerline{\vbox{\offinterlineskip
  6975.   \hrule
  6976.   \halign{&\vrule#&\strut\hfil#\hfil&\quad\tt#\quad&\quad\tt#\quad&
  6977.       \quad\tt#\quad&\quad\tt#\quad&\vrule#\cr
  6978.     height2pt&\multispan5 &height2pt\cr
  6979.     &\omit\strut Magnif-\hfil&\multispan2\hfil Packed Pixels\hfil&
  6980.       \multispan2\hfil Unpacked Pixels\hfil&\cr
  6981.     &\omit\strut\hfil ication&\hfil\rm Flat\hfil&\hfil\rm Nested\hfil&
  6982.         \hfil\rm Flat\hfil&\hfil\rm Nested\hfil&\cr
  6983.     height2pt&\multispan5 &height2pt\cr
  6984.     \noalign{\hrule}
  6985.     height2pt&\multispan5 &height2pt\cr
  6986.     &0&CMR10.300PK&[300]CMR10.PK&CMR10.1500PXL&[1500]CMR10.PXL&\cr
  6987.     &$1\over2$&CMR10.329PK&[329]CMR10.PK&CMR10.1643PXL&[1643]CMR10.PXL&\cr
  6988.     &1&CMR10.360PK&[360]CMR10.PK&CMR10.1800PXL&[1800]CMR10.PXL&\cr
  6989.     &2&CMR10.432PK&[432]CMR10.PK&CMR10.2160PXL&[2160]CMR10.PXL&\cr
  6990.     height2pt&\multispan5 &height2pt\cr}
  6991.   \hrule
  6992. }}
  6993.  
  6994. The program determines which class(es) of files are available from the
  6995. qualifiers \.{/PXL\_FONT\_DIRECTORY} (for the unpacked files) and
  6996. \.{/PK\_FONT\_DIRECTORY} (for the packed files).  At least one of these
  6997. qualifiers must be present; if the qualifier provided is a VMS logical name,
  6998. it has been translated repeatedly until a VMS directory specification resulted.
  6999. Examination of (the first translation of) this specification then determines
  7000. whether the flat or nested structure is being used: if the directory
  7001. specification ends with the characters `\.{.]}' it indicates that the raster
  7002. files will be found in sub-directories, whilst absence of the `\..' indicates
  7003. a flat structure.  The variables |pk_rooted| and |pxl_rooted| will already
  7004. have been set to reflect this.
  7005.  
  7006. Whether packed or unpacked pixel files are used, it is necessary to
  7007. compute a number which relates the magnification to either the sub-directory
  7008. name or the file extension.  Since actual file names may well have been
  7009. computed using low-accuracy (possibly integer) arithmetic by operating system
  7010. commands, the possibility exists that the numbers may have been truncated to
  7011. the ``wrong'' integer compared with those that \.{DVItoLN03} computes;
  7012. therefore, we always arrange to look for the files using both the computed
  7013. number and those $\pm1$ from it.
  7014.  
  7015. The variable |dir_off| is used to compute the offset from the central figure,
  7016. which is held in |p|.  The variables |file_stat| and |find_ctxt| are used in
  7017. conjunction with Vax/VMS system calls to determine whether the requested file
  7018. exists; |found_file| takes the values $-1$, indicating that the file wasn't
  7019. found, or $+1$ if it is found.  It also can take the value $0$ whilst the
  7020. search is proceding, so a simple boolean cannot be used instead.
  7021.  
  7022. The following procedure converts the \TeX\ font referenced by |TeX_font| into
  7023. the correct strings for the directory and file name under which \.{DVItoLN03}
  7024. expects to find the pixel file.
  7025.  
  7026. @p procedure name_pxl_file ( @!TeX_font :  integer;@/
  7027.                              var @!open_file_name: file_spec;
  7028.                              var @!what_it_was: pixel_types);
  7029.    var @!p,@!dir_off,@!file_stat,@!find_ctxt,@!found_file : integer;@/
  7030.        @!found_name,@!expected_file_spec : file_spec;
  7031. begin
  7032.   found_file := -1;
  7033.   expected_file_spec:='';@/
  7034.   @<Locate packed pixel file@>;@/
  7035.   if found_file<0 then
  7036.     @<Locate unpacked pixel file@>;
  7037.   if found_file<0 then
  7038.   begin
  7039.     warning('cannot find file ',expected_file_spec);
  7040. @:Warning: cannot find file}{\quad\.{cannot find file}@>
  7041.     what_it_was:=unavailable
  7042.   end
  7043. end;
  7044.  
  7045.  
  7046. @ We only look for packed pixel files if the \.{/PK\_FONT\_DIRECTORY}
  7047. qualifier was present, in which case its value will have been placed into the
  7048. string |tex_pk_font|. If the final characters of (the first logical name
  7049. translation of) this string were `\.{.]}', then a nested directory structure
  7050. is assumed (as indicated in |pk_rooted|), and the file is sought in the form
  7051. \hbox{\meta{disk}\meta{directory}\.[\meta{size}\.]\meta{name}\.{.PK}}.
  7052. Alternatively, if the directory is flat, the filename sought will be
  7053. \hbox{\meta{disk}\meta{directory}\meta{name}\..\meta{size}\.{PK}}.
  7054.  
  7055. Unfortunately, it is necessary to reconstruct the desired string for each
  7056. offset tried.  The \meta{size} is effectively computed from 1.5
  7057. times the font design size compared with the \TeX ware default of 200
  7058. pixels/inch, yielding 300 for unmagnified fonts.
  7059.  
  7060. @d append_name==for n:=font_name[TeX_font] to font_name[TeX_font+1]-1 do
  7061.                   open_file_name:=open_file_name+xchr[names[n]]
  7062.  
  7063. @d flat_file_spec(#)==begin open_file_name := # ;
  7064.                       append_name;
  7065.                       open_file_name:=open_file_name+'.'+str_int(p+dir_off)
  7066.                       end
  7067.  
  7068. @d rooted_file_spec(#)==begin open_file_name:= # + '[' +
  7069.                                    str_int(p+dir_off) + ']';
  7070.                         append_name;
  7071.                         open_file_name:=open_file_name+'.'
  7072.                         end
  7073.  
  7074. @<Locate packed pixel file@>==
  7075. if tex_pk_font.length > 0 then
  7076. begin
  7077.   dir_off:=0;
  7078.   p:=round(((resolution/1000)*mag*font_scaled_size[TeX_font])/font_design_size[TeX_font]);
  7079.   repeat
  7080.     if not pk_rooted then
  7081.       flat_file_spec(tex_pk_font)
  7082.     else
  7083.       rooted_file_spec(tex_pk_font);
  7084.     open_file_name:=open_file_name+'PK';
  7085.     @<Attempt to find file with specification |open_file_name|@>
  7086.   until found_file<>0;
  7087.   if found_file>0 then
  7088.   begin
  7089.       open_file_name:=found_name;
  7090.       what_it_was:=packed_file
  7091.   end
  7092. end;
  7093.  
  7094. @ To determine the correct file of pixels to be read, the quantity |p| is
  7095. computed; since it is a \TeX ware convention that all pixel files are assumed
  7096. (at magnification=1000) to be at a |resolution| of 200 dots/inch, and the
  7097. LN03's |resolution| is \&{300} dots/inch, then an ordinary unmagnified font
  7098. will be sought with a
  7099. $\langle$\\{size}$\rangle$ of \.{1500}, whilst one magnified \.{\BS magstep 1}
  7100. will have a $\langle$\\{size}$\rangle$ of \.{1800}, \\{etc.}   Computation of
  7101. |p| yields the appropriate value for the Vax/VMS directory or file type to be
  7102. used, and |VAX_lib_find_file| is used to verify that the file exists; to allow
  7103. for rounding errors, the directories |p|$\pm1$ are also sought.
  7104.  
  7105. @<Locate unpacked pixel file@>==
  7106. if tex_pxl_font.length > 0 then
  7107. begin
  7108.   dir_off:=0;
  7109.   p:=round(((resolution/200)*mag*font_scaled_size[TeX_font])/font_design_size[TeX_font]);
  7110.   repeat
  7111.     if not pxl_rooted then
  7112.       flat_file_spec(tex_pxl_font)
  7113.     else
  7114.       rooted_file_spec(tex_pxl_font);
  7115.     open_file_name:=open_file_name+'PXL';
  7116.     @<Attempt to find file with specification |open_file_name|@>
  7117.   until found_file<>0;
  7118.   if found_file>0 then
  7119.   begin
  7120.     open_file_name:=found_name;
  7121.     what_it_was:=pixel_file
  7122.   end
  7123. end
  7124.  
  7125. @ Now that we've got a full file specification (barring the version number) in
  7126. |open_file_name|, we call upon the Vax/VMS system services to try to locate
  7127. the file.  Before doing this, we make a note of the file specification if it's
  7128. that for the default file name (with |dir_off = 0|), and we haven't
  7129. previously noted a file name.
  7130.  
  7131. The find\_file system service requires a context variable (because it is
  7132. capable of finding a number of files matching a `wild-carded' file
  7133. specification), but we're not interested in that; therefore we always set
  7134. |find_ctxt| to zero before searching.  \&{N.B.} This may not be written as the
  7135. constant |0| in the procedure call, because the system service writes a value
  7136. back to this variable.
  7137.  
  7138. The system service returns an odd status if the search is successful;
  7139. otherwise we change |dir_off| to a suitable new value, unless we've already
  7140. tried |p| and $p\pm1$, in which case we indicate the absence of the file by
  7141. setting |found_file = -1|.
  7142.  
  7143. @<Attempt to find file with specification |open_file_name|@>=
  7144. if (dir_off=0) and (expected_file_spec='') then
  7145.   expected_file_spec:=open_file_name;
  7146. find_ctxt:=0;
  7147. file_stat:=VAX_lib_find_file(open_file_name,found_name,find_ctxt);
  7148. found_file:=0;
  7149. if odd(file_stat) then found_file:=1
  7150. else
  7151. begin
  7152.   if dir_off=1 then found_file:=-1;
  7153.   if dir_off=-1 then dir_off:=1 else dir_off:=-1
  7154. end
  7155.  
  7156. @ Although up to |max_lnfonts| may be downloaded by this program, the LN03 is
  7157. only capable of accessing any ten of these at any one time; it accomplishes
  7158. this by allocating the |SGR| (Select Graphic Rendition) designators |10..19|
  7159. to the first ten fonts downloaded.  Fonts are designated by means of their
  7160. 16-character internal font identifiers, allocated during the generation of
  7161. each font.
  7162.  
  7163. @<Designate first 10 fonts to SGRs |10..19|@>=
  7164. for m:=0 to VAX_min(max_SGR,next_lnf-1) do      {Match first 10 fonts to |SGR| designators}
  7165. begin font_in_use[m]:=m; who_uses[m]:=m;
  7166.   write_ln(ln3_file,dcs,'1;1',m:1,'}U00000',m:1,'002SK00GG',st)
  7167. end;
  7168. write_ln(ln3_file,csi,'10m');   {|SGR| 10 --- select most used font}
  7169. cur_out:=0              {We won't need to |SGR| if first output uses |SGR| 10}
  7170.  
  7171. @*Format of Virtual Fonts.  The following description is cribbed almost
  7172. verbatim from Knuth's description of \.{VFtoVP}.
  7173. @^Knuth, D.~E.@>
  7174. @:VFtoVP program}{\.{VFtoVP} program@>
  7175.  
  7176. The idea behind \.{VF} files is that a general
  7177. interface mechanism is needed to switch between the myriad font
  7178. layouts provided by different suppliers of typesetting equipment.
  7179. Without such a mechanism, people must go to great lengths writing
  7180. inscrutable macros whenever they want to use typesetting conventions
  7181. based on one font layout in connection with actual fonts that have
  7182. another layout. This puts an extra burden on the typesetting system,
  7183. interfering with the other things it needs to do (like kerning,
  7184. hyphenation, and ligature formation).
  7185.  
  7186. These difficulties go away when we have a ``virtual font,''
  7187. i.e., a font that exists in a logical sense but not a physical sense.
  7188. A typesetting system like \TeX\ can do its job without knowing where the
  7189. actual characters come from; a device driver can then do its job by
  7190. letting a \.{VF} file tell what actual characters correspond to the
  7191. characters \TeX\ imagined were present. The actual characters
  7192. can be shifted and/or magnified and/or combined with other characters
  7193. from many different fonts. A virtual font can even make use of characters
  7194. from virtual fonts, including itself.
  7195.  
  7196. Virtual fonts also allow convenient character substitutions for proofreading
  7197. purposes, when fonts designed for one output device are unavailable on another.
  7198.  
  7199. @ A \.{VF} file is organized as a stream of 8-bit bytes, using conventions
  7200. borrowed from \.{DVI} and \.{PK} files. Thus, a device driver that knows
  7201. about \.{DVI} and \.{PK} format will already contain most of the mechanisms
  7202. necessary to process \.{VF} files.
  7203.  
  7204.  
  7205. A preamble appears at the beginning, followed by a sequence of character
  7206. definitions, followed by a postamble. More precisely, the first byte of
  7207. every \.{VF} file must be the first byte of the following ``preamble
  7208. command'':
  7209.  
  7210. \yskip\hang|pre| 247 |i[1]| |k[1]| |x[k]| |cs[4]| |ds[4]|.
  7211. Here |i| is the identification byte of \.{VF}, currently 202. The string
  7212. |x| is merely a comment, usually indicating the source of the \.{VF} file.
  7213. Parameters |cs| and |ds| are respectively the check sum and the design size
  7214. of the virtual font; they should match the first two words in the header of
  7215. the \.{TFM} file for the virtual font.
  7216.  
  7217. \yskip
  7218. After the |pre| command, the preamble continues with font definitions;
  7219. every font needed to specify ``actual'' characters in later
  7220. \\{set\_char} commands is defined here. The font definitions are
  7221. exactly the same in \.{VF} files as they are in \.{DVI} files, except
  7222. that the scaled size |s| is relative and the design size |d| is absolute:
  7223.  
  7224. \yskip\hang|fnt_def1| 243 |k[1]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
  7225. Define font |k|, where |0<=k<256|.
  7226.  
  7227. \yskip\hang|@!fnt_def2| 244 |k[2]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
  7228. Define font |k|, where |0<=k<65536|.
  7229.  
  7230. \yskip\hang|@!fnt_def3| 245 |k[3]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
  7231. Define font |k|, where |0<=k<@t$2^{24}$@>|.
  7232.  
  7233. \yskip\hang|@!fnt_def4| 246 |k[4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
  7234. Define font |k|, where |@t$-2^{31}$@><=k<@t$2^{31}$@>|.
  7235.  
  7236. \yskip\noindent
  7237. These font numbers |k| are ``local''; they have no relation to font numbers
  7238. defined in the \.{DVI} file that uses this virtual font. The dimension~|s|,
  7239. which represents the scaled size of the local font being defined,
  7240. is a |fix_word| relative to the design size of the virtual font.
  7241. Thus if the local font is to be used at the same size
  7242. as the design size of the virtual font itself, |s| will be the
  7243. integer value $2^{20}$. The value of |s| must be positive and less than
  7244. $2^{24}$ (thus less than 16 when considered as a |fix_word|).
  7245. The dimension~|d| is a |fix_word| in units of printer's points; hence it
  7246. is identical to the design size found in the corresponding \.{TFM} file.
  7247.  
  7248. @d vf_id_byte=202
  7249.  
  7250. @<Glob...@>=
  7251. @!vf_file:byte_file;
  7252.  
  7253. @ The preamble is followed by zero or more character packets, where each
  7254. character packet begins with a byte that is $<243$. Character packets have
  7255. two formats, one long and one short:
  7256.  
  7257. \yskip\hang|long_char| 242 |pl[4]| |cc[4]| |tfm[4]| |dvi[pl]|. This long form
  7258. specifies a virtual character in the general case.
  7259.  
  7260. \yskip\hang|short_char0..short_char241|
  7261. |pl[1]| |cc[1]| |tfm[3]| |dvi[pl]|. This short form specifies a
  7262. virtual character in the common case
  7263. when |0<=pl<242| and |0<=cc<256| and $0\le|tfm|<2^{24}$.
  7264.  
  7265. \yskip\noindent
  7266. Here |pl| denotes the packet length following the |tfm| value; |cc| is
  7267. the character code; and |tfm| is the character width copied from the
  7268. \.{TFM} file for this virtual font. There should be at most one character
  7269. packet having any given |cc| code.
  7270.  
  7271. The |dvi| bytes are a sequence of complete \.{DVI} commands, properly
  7272. nested with respect to |push| and |pop|. All \.{DVI} operations are
  7273. permitted except |bop|, |eop|, and commands with opcodes |>=243|.
  7274. Font selection commands (|fnt_num0| through |fnt4|) must refer to fonts
  7275. defined in the preamble.
  7276.  
  7277. Dimensions that appear in the \.{DVI} instructions are analogous to
  7278. |fix_word| quantities; i.e., they are integer multiples of $2^{-20}$ times
  7279. the design size of the virtual font. For example, if the virtual font
  7280. has design size $10\,$pt, the \.{DVI} command to move down $5\,$pt
  7281. would be a \\{down} instruction with parameter $2^{19}$. The virtual font
  7282. itself might be used at a different size, say $12\,$pt; then that
  7283. \\{down} instruction would move down $6\,$pt instead. Each dimension
  7284. must be less than $2^{24}$ in absolute value.
  7285.  
  7286. Device drivers processing \.{VF} files treat the sequences of |dvi| bytes
  7287. as subroutines or macros, implicitly enclosing them with |push| and |pop|.
  7288. Each subroutine begins with |w=x=y=z=0|, and with current font~|f| the
  7289. number of the first-defined in the preamble (undefined if there's no
  7290. such font). After the |dvi| commands have been
  7291. performed, the |h| and~|v| position registers of \.{DVI} format and the
  7292. current font~|f| are restored to their former values;
  7293. then, if the subroutine has been invoked by a \\{set\_char} or \\{set}
  7294. command, |h|~is increased by the \.{TFM} width
  7295. (properly scaled)---just as if a simple character had been typeset.
  7296.  
  7297. (The following commands, already defined as macros with respect to the
  7298. \.{DVI} file, have the same interpretation when they appear in a virtual
  7299. font file: \hang |set_char_0|, |set1|, |set_rule|, |put1|, |put_rule|, |nop|,
  7300. |push|, |pop|, |right1|, |w0|, |w1|, |x0|, |x1|, |down1|, |y0|, |y1|, |z0|,
  7301. |z1|, |fnt_num_0|, |fnt1|, |xxx1|, |xxx4|, |fnt_def1|, |pre|, |post|.)
  7302.  
  7303. @d long_char=242 {\.{VF} command for general character packet}
  7304. @d improper_DVI_for_VF==139,140,243,244,245,246,247,248,249,250,251,252,
  7305.     253,254,255
  7306.  
  7307. @ The character packets are followed by a trivial postamble, consisting of
  7308. one or more bytes all equal to |post| (248). The total number of bytes
  7309. in the file should be a multiple of~4.
  7310.  
  7311. @ Here are some functions, analogous to those for the |dvi_file|, for
  7312. reading one or more bytes from the |vf_file|.
  7313.  
  7314. Note that variable |flushing| is used to suppress the warning (when |eof| is
  7315. met on the |vf_file|) if bytes are merely being read to discard the
  7316. postamble.
  7317.  
  7318. @p procedure read_vf_file(var x:eight_bits);
  7319.   label 9999;
  7320.   begin while vf_count >= VAX_block_length do
  7321.     begin get(vf_file,VAX_continue); vf_count:=vf_count-VAX_block_length;
  7322.       incr(vf_block);
  7323.       if eof(vf_file) then
  7324.       begin if not flushing then
  7325.           warning('VF file ended without postamble!');
  7326. @:Warning: VF file ended without postamble}{\quad\.{VF file ended without postamble}@>
  7327.         x := post; goto 9999
  7328.       end
  7329.     end;
  7330.     x := vf_file^[vf_count];
  7331.     incr(vf_count); incr(vf_loc);
  7332. 9999:
  7333.   end;
  7334. @#
  7335. function @!vf_one: integer;  {returns the next byte, unsigned}
  7336.   var @!b: eight_bits;
  7337. begin
  7338.     read_vf_file(b); vf_one:=b
  7339. end;
  7340. @#
  7341. function vf_byte:integer; {returns the next byte, signed}
  7342. var b:eight_bits;
  7343. begin read_vf_file(b);
  7344. if b<128 then vf_byte:=b @+ else vf_byte:=b-256;
  7345. end;
  7346. @#
  7347. function vf_two:integer; {returns the next two bytes, unsigned}
  7348. var a,@!b:eight_bits;
  7349. begin read_vf_file(a); read_vf_file(b);
  7350. vf_two:=a*256+b;
  7351. end;
  7352. @#
  7353. function vf_pair:integer; {returns the next two bytes, signed}
  7354. var a,@!b:eight_bits;
  7355. begin read_vf_file(a); read_vf_file(b);
  7356. if a<128 then vf_pair:=a*256+b
  7357. else vf_pair:=(a-256)*256+b;
  7358. end;
  7359. @#
  7360. function vf_three:integer; {returns the next three bytes, unsigned}
  7361. var a,@!b,@!c:eight_bits;
  7362. begin read_vf_file(a); read_vf_file(b); read_vf_file(c);
  7363. vf_three:=(a*256+b)*256+c;
  7364. end;
  7365. @#
  7366. function vf_trio:integer; {returns the next three bytes, signed}
  7367. var a,@!b,@!c:eight_bits;
  7368. begin read_vf_file(a); read_vf_file(b); read_vf_file(c);
  7369. if a<128 then vf_trio:=(a*256+b)*256+c
  7370. else vf_trio:=((a-256)*256+b)*256+c;
  7371. end;
  7372. @#
  7373. function vf_quad:integer; {returns the next four bytes, signed}
  7374. var a,@!b,@!c,@!d:eight_bits;
  7375. begin read_vf_file(a); read_vf_file(b); read_vf_file(c); read_vf_file(d);
  7376. if a<128 then vf_quad:=((a*256+b)*256+c)*256+d
  7377. else vf_quad:=(((a-256)*256+b)*256+c)*256+d;
  7378. end;
  7379.  
  7380. @ Here are the globals required to manipulate the VF file.
  7381.  
  7382. @<Glob...@>=
  7383. @!vf_count, @!vf_block, @!vf_loc : integer;
  7384. @!flushing : boolean;
  7385.  
  7386. @*Handling Virtual Fonts. Before we look around the fonts, we'd better see
  7387. if any of the fonts so far loaded are virtual ones; this can be determined
  7388. by whether a \.{VF} file exists for the given font.
  7389.  
  7390. We use a |while| loop, because the analysis of a virtual font most probably
  7391. will result in the addition of one or more new fonts, some of which may
  7392. themselves be virtual; to prevent infinite recursion with a self-referential
  7393. font, such additional fonts are only loaded if glyphs within the font are
  7394. activated.
  7395.  
  7396. @<Remap any virtual fonts@>=
  7397. TeX_font:=0; vf_ptr:=0;
  7398. while TeX_font < nf do  {Currently defined fonts, and any this defines anew}
  7399. begin
  7400. if font_type[TeX_font]=wanted then
  7401.   @<Locate virtual font metrics@>;
  7402. incr(TeX_font)
  7403. end;
  7404.  
  7405. @ This module determines the name of the \.{VF} file, if it exists,
  7406. corresponding to the current \TeX\ font.
  7407.  
  7408. @<Locate virtual font metrics@>=
  7409. if not no_virt_support then
  7410. begin
  7411.   file_name:='';
  7412.   for n:=font_name[TeX_font] to font_name[TeX_font+1]-1 do
  7413.     if (names[n]>="a") and (names[n]<="z") then
  7414.       file_name:=file_name+xchr[names[n]+"A"-"a"]
  7415.     else
  7416.       file_name:=file_name+xchr[names[n]];
  7417.   open(vf_file,file_name,@=readonly@>,@=access_method@>:=@=direct@>,
  7418.        @=user_action@>:=file_open,@=default@>:=tex_virtual+'.VF;0',
  7419.        VAX_continue);
  7420.   file_stat:=status(vf_file);
  7421.   if (file_stat<>0) and (file_stat<>3) then
  7422.   begin
  7423.     case file_stat of
  7424.       2: file_message := 'PAS-E-ERRDUROPE';
  7425.       5: file_message := 'PAS-E-ACCMETINC';
  7426.       6: file_message := 'PAS-E-RECLENINC';
  7427.       7: file_message := 'PAS-E-RECTYPINC';
  7428.       8: file_message := 'PAS-E-ORDSPEINC';
  7429.     othercases
  7430.       file_message := str_int(file_stat);
  7431.     endcases;
  7432.     abort('Couldn''t open virtual font ',file_name,'. Status=',file_message);
  7433. @:fatal error Couldn't open virtual font}{\quad\.{Couldn't open virtual font}@>
  7434.   end
  7435.   else if file_stat=0 then
  7436.   begin
  7437.     reset(vf_file);
  7438.     vf_block:=0; vf_count:=0; vf_loc:=0;
  7439.     font_type[TeX_font]:=virtual;
  7440.     open_file_name:=def_file_name;@/
  7441.     @<Find name and magnification of font file@>;@/
  7442.     @<Report name of font file in use@>;@/
  7443.     print_ln(' [virtual]');
  7444.     write_ln(term_out,' is virtual',crlf);@/
  7445.     read_VF;@/
  7446.     close(vf_file,@=disposition@>:=@=save@>,VAX_continue);
  7447.   end;
  7448. end
  7449.  
  7450. @ Globals for storing virtual font files: unlike \.{VFtoVP},
  7451. @.VFtoVP@>
  7452. we don't read the \\{entire} file into memory.  We do, however, store those
  7453. |dvi| bytes that belong to some wanted character packets, but only for
  7454. glyphs which are more complex than simply imaging a character from some
  7455. other font; this is efficient, and also feasible, since VF files are never
  7456. very large (unlike physical font files!) Much of this code is, once again,
  7457. lifted from \.{VFtoVP}.
  7458.  
  7459. The array |vf| is global, along with the pointer into it, |vf_ptr|, because
  7460. we accumulate therein all setting sequences read from all virtual font files
  7461. processed.  Most of the other variables are only required when actually
  7462. reading a particular virtual font, so are local to |read_VF|.
  7463.  
  7464. \yskip\hang |vf_take| When |dvi| bytes are to be ``read'' from array |vf|, the
  7465. variable |vf_take| will be non-negative and hence index an element of the
  7466. array; during normal input from the \.{DVI} file, a negative value ensures
  7467. that input is taken from the file itself.
  7468.  
  7469. \yskip\hang |TeX_font| will always point to the current virtual font being
  7470. processed, amongst all the fonts (both virtual and physical) that are in
  7471. the usual data structures.  This global variable has the same name as
  7472. parameters of the procedures |name_pxl_file| and |add_txf_to_lnf|, in order
  7473. that common sections of code may be shared from the W{\mc EB}.
  7474.  
  7475. \yskip\hang |font_type| records whether a font is |virtual| or physical (in which
  7476. case the value |wanted| will have been set when the font's \.{TFM} file was
  7477. first read.
  7478.  
  7479. @<Glob...@>=
  7480. @!vf:packed array[0..vf_size] of eight_bits; {the \.{VF} input data goes here}
  7481. @!vf_ptr:0..vf_size; {first unused location in |vf|}
  7482. @!vf_take:-1..vf_size; {next byte to be ``read'' from |vf|}
  7483. @!TeX_font:0..max_fonts; {index into font metrics database for current virtual}
  7484. @!font_type: array [0..max_fonts] of download_status;
  7485.  
  7486. @ To ensure that we start by reading genuine |dvi| bytes from the \.{DVI}
  7487. file, we must make |vf_take| negative.
  7488.  
  7489. @<Preset...@>==
  7490. vf_take:=-1;
  7491.  
  7492. @ Here's the main procedure for reading virtual font files.  It exists as a
  7493. procedure purely in order that local variable declarations can be made to
  7494. cater for those code sections which are also invoked within |define_font|.
  7495.  
  7496. When we encounter a font definition in the \.{VF} file, we read its
  7497. associated \.{TFM} file in the usual fashion; we associate a ``\TeX\ font
  7498. number'' with this font, determined from the highest font previously used
  7499. within the previous actual or virtual fonts, via the variable |font_base|.
  7500.  
  7501. @p procedure read_VF;
  7502. var @!f: 0..max_fonts; {Local index}
  7503.   @!p: integer; {length of the area/directory spec}
  7504.   @!n: integer; {length of the font name proper}
  7505.   @!c,@!q,@!d: integer; {check sum, scaled size, design size}
  7506.   @!r: 0..name_length; {index into |cur_name|}
  7507.   @!j,@!k: 0..name_size; {indices into |names|}
  7508.   @!mismatch: boolean; {names differ}
  7509.   @!font_base: 0..max_fonts; {offset for local font numbers}
  7510.   @!z,@!alpha,@!beta: integer; {to facilitate |fix_word| multiplications}
  7511.   @!font_ref:array[0..max_fonts] of integer; {local font numbers}
  7512.   @!font_ext:array[0..max_fonts] of integer; {``external'' font numbers}
  7513.   @!font_ptr:0..max_fonts; {number of local fonts}
  7514.   @!packet_start:array[0..255] of 0..vf_size; {character packet boundaries}
  7515.   @!packet_found:boolean; {at least one packet has appeared}
  7516.   @!temp_byte:eight_bits;@+@!temp_int:integer; {registers for simple calculations}
  7517.   @!pl:integer; {packet length}
  7518.   @<Declare |read_VF|'s local procedures@>@;
  7519. begin
  7520.   font_base:=highest_font+1; flushing:=false;@/
  7521.   @<Read the whole \.{VF} file@>;
  7522. end;
  7523.  
  7524. @ Again we cautiously verify that we've been given decent data.
  7525.  
  7526. @d vf_abort(#)==
  7527.   abort('(VF file) ',#,crlf)
  7528. @!@:fatal error VF file ...}{\quad\.{(VF file)}@>
  7529.  
  7530. @<Read the whole \.{VF} file@>=
  7531. temp_byte:=vf_one;
  7532. if temp_byte<>pre then vf_abort('First byte isn''t `pre''');
  7533. @:fatal error VF file First byte}{\qquad\.{First byte isn't \it pre}@>
  7534. @<Read the preamble command@>;
  7535. @<Read and store the font definitions and character packets@>;
  7536. @<Read and verify the postamble@>
  7537.  
  7538. @ Here's where we read the preamble of the virtual font file.  Note that we
  7539. make the assumption throughout that a `DVI unit' is \.{1sp} (as it always is
  7540. in output generated by \TeX), so that $\hbox{`DVI unit'}=16\hbox{\.{fix}}$.
  7541.  
  7542. @<Read the preamble command@>=
  7543. temp_byte:=vf_one;
  7544. if temp_byte<>vf_id_byte then vf_abort('Wrong version number in second byte!');
  7545. @:fatal error VF file Wrong VF version}{\qquad\.{Wrong VF version...}@>
  7546. temp_byte:=vf_one;  {read the length of introductory comment}
  7547. for k:=1 to temp_byte do temp_int:=vf_one; {and discard it}
  7548. temp_int:=vf_quad;
  7549. if (temp_int<>0) and (font_check_sum[TeX_font]<>0) and
  7550.    (temp_int<>font_check_sum[TeX_font]) then
  7551.   error('VF checksum disagreed! TFM:',font_check_sum[TeX_font]:1,
  7552.            ' VF:',temp_int:1);
  7553. @:Error: VF checksum disagreed}{\quad\.{VF checksum disagreed}@>
  7554. z:=vf_quad div 16; {convert to |dvi| units}
  7555. if z<>font_design_size[TeX_font] then
  7556.   error('VF design size disagreed! TFM:',font_design_size[TeX_font]:1,
  7557.            ' VF:',z:1);
  7558. @:Error: VF design size disagreed}{\quad\.{VF design size disagreed}@>
  7559. z:=font_scaled_size[TeX_font]; {but we're using it at this size}
  7560. @<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>;
  7561.  
  7562. @ The remainder of the file will consist of font definitions and character
  7563. packets; once any of the latter has been met, it is not legal for the former
  7564. to appear again.
  7565.  
  7566. @<Read and store the font definitions and character packets@>=
  7567. for k:=0 to 255 do packet_start[k]:=vf_size;
  7568. font_ptr:=0; packet_found:=false;
  7569. repeat temp_byte:=vf_one;
  7570.   if temp_byte<>post then
  7571.     if temp_byte>long_char then @<Read and store a font definition@>
  7572.     else @<Read and store a character packet@>;
  7573. until temp_byte=post
  7574.  
  7575. @ The postamble of the virtual font file should consist purely of |post|
  7576. bytes; furthermore, the file should be a multiple of four bytes long.
  7577.  
  7578. @<Read and verify the postamble@>=
  7579. flushing:=true;
  7580. while (temp_byte=post)and not eof(vf_file) do temp_byte:=vf_one;
  7581. if not eof(vf_file) then
  7582.   warning('extra junk at end of the VF file.');
  7583. @:Warning: extra junk at end of VF file}{\quad\.{extra junk...VF file}@>
  7584. if vf_loc mod 4 <> 0 then
  7585.   warning('VF data not a multiple of 4 bytes')
  7586. @:Warning: VF data not a multiple of 4 bytes}{\quad\.{VF data not a multiple of 4 bytes}@>
  7587.  
  7588. @ The font definition reads as follows:
  7589.  
  7590. \yskip\hang|fnt_defN| 243..6 |k[1..4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
  7591.  
  7592. \noindent where |k| is a local font number; this is used as an offset from
  7593. |font_base| when reading the associated \.{TFM} file.
  7594.  
  7595. @<Read and store a font definition@>=
  7596. begin if packet_found or(temp_byte>=pre) then
  7597.   vf_abort('Illegal byte ',temp_byte:1,' at start of character packet!');
  7598. @:fatal error VF file Illegal byte}{\qquad\.{Illegal byte...at start...}@>
  7599. case temp_byte of
  7600.  fnt_def1: font_ref[font_ptr]:=vf_one;
  7601.  fnt_def1+1: font_ref[font_ptr]:=vf_two;
  7602.  fnt_def1+2: font_ref[font_ptr]:=vf_three;
  7603.  fnt_def1+3: font_ref[font_ptr]:=vf_quad {there are no other cases}
  7604. endcases;@/
  7605. font_ext[font_ptr]:=font_base+font_ptr; {invent ``external'' font number}
  7606. if font_ptr=max_fonts then vf_abort('References too many fonts!');
  7607. @:fatal error VF file References}{\qquad\.{References too many fonts}@>
  7608. @<Move the font parameters into position for font |nf|,
  7609.       and print the font name@>;
  7610. print(' (local font ',font_ref[font_ptr]:1,' of font ',font_num[TeX_font]:1,')');
  7611. if highest_font<font_ext[font_ptr] then
  7612.   highest_font:=font_ext[font_ptr];
  7613. @<Avoid reloading the font metrics...@>;
  7614. if f<nf then
  7615. begin font_ext[font_ptr] := font_num[f];
  7616.   print_ln('---is identical to font ',font_num[f]:1);
  7617.   incr(nf)
  7618. end else
  7619.   @<Read the local font's \.{TFM} file and record the characters it contains@>;
  7620. incr(font_ptr);
  7621. end
  7622.  
  7623. @ We need to position some of this information in a similar fashion to when
  7624. \.{TFM} files are read during normal font definitions from the \.{DVI} file:
  7625.  
  7626. @<Move the font parameters into position for font |nf|,
  7627.     and print the font name@>=
  7628. if nf = max_fonts then
  7629.   capacity_exceeded('too many fonts (max fonts=',max_fonts:1,')');
  7630. @:capacity exceeded too many fonts}{\quad\.{too many fonts}@>
  7631. font_num[nf]:=font_ext[font_ptr]; {record invented ``external'' font number}
  7632. font_map[nf]:=nf; {until we know better, this font is new}
  7633. c:=vf_quad; font_check_sum[nf]:=c; {|c[4]|}
  7634. q:=vf_quad; font_scaled_size[nf]:=q; {|s[4]|}
  7635. if (q<0) or (q>@'77777777) then {|s| is negative or exceeds $2^{24}-1$}
  7636.   vf_abort('Mapped font size is too big!');
  7637. @:fatal error VF file Mapped font size too big}{\qquad\.{Mapped font size too big}@>
  7638. d:=vf_quad div 16; font_design_size[nf]:=d; {|d[4]| in sp}
  7639. @<Determine actual scaling for physical font@>;
  7640. font_scaled_size[nf]:=q; {store revised scaling}
  7641. p:=vf_one; {|a[1]|}
  7642. n:=vf_one; {|l[1]|}
  7643. if font_name[nf]+n+p>name_size then
  7644.   capacity_exceeded('too many names (name size=',name_size:1,')');
  7645. @:capacity exceeded too many names}{\quad\.{too many names}@>
  7646. font_name[nf+1]:=font_name[nf]+n+p;
  7647. print('Font ',font_num[nf]:1,': ');
  7648. if n+p=0 then print('null font name!')
  7649. @.null font name@>
  7650. else
  7651.   for k:=font_name[nf] to font_name[nf+1]-1 do names[k]:=lower(vf_one);
  7652. incr(nf); print_font(nf-1); decr(nf); f:=nf;
  7653.  
  7654. @ When a |fnt_defN| is read from a virtual font file, the parameter |s| (in
  7655. variable |q|)  represents the scaled size relative to the design size of the
  7656. virtual font. The latter was stored (in |font_design_size[TeX_font]|; scaled
  7657. by $2^{-4}$) when the original definition of the virtual font was read from
  7658. the \.{DVI} file, and has been checked during the reading of the virtual
  7659. font's preamble.  Therefore, we will later call |in_TFM| to read the \.{TFM}
  7660. file for the physical font and adjust all dimensions by the appropriate
  7661. amount.  To do this, we have to compute a new |q| by performing ``correct''
  7662. arithmetic on the expression $\hbox{|font_design_size[TeX_font]|}\times q$.
  7663.  
  7664. We utilize the same algorithm as is used for converting the width information
  7665. in a \.{TFM} file; local variable |z| has already been set to the virtual
  7666. font's design size, and converted to $|z|^\prime$, so we just need to split up
  7667. the value |s| into its constituent bytes and perform the multiplications.
  7668.  
  7669. By applying a cast to treat |q| as |unsigned|, we ensure that |b0<>0| for
  7670. negative values of |q|.
  7671.  
  7672. @d VAX_unsigned_type == unsigned
  7673.  
  7674. @<Determine actual scaling for physical font@>=
  7675. b0:=(q::VAX_unsigned_type div @'100000000)::eight_bits;
  7676. b1:=(q mod @'100000000) div @'200000;
  7677. b2:=(q mod @'200000) div @'400; b3:=q mod @'400;
  7678. q:=(((((b3*z) div @'400)+(b2*z)) div @'400)+(b1*z))div beta;
  7679. if b0>0 then q:=q-alpha; {assume $|q|<2^{25}$}
  7680.  
  7681.  
  7682. @ @<Read the local font's \.{TFM} file...@>=
  7683. begin
  7684.   @<Move font name into the |cur_name| string@>;
  7685.   open_tfm_file;
  7686.   font_bc[nf]:=0; font_ec[nf]:=256; {will cause error if not modified soon}
  7687.   if in_TFM(q) then @<Finish loading the new font info@>;
  7688.   if font_bc[nf]<=font_ec[nf] then
  7689.     if font_ec[nf]>255 then abort('---not loaded, bad TFM file!');
  7690. @:fatal error Font not loaded Bad TFM file}{\qquad\.{Bad TFM file}@>
  7691.   print_ln('')
  7692. end
  7693.  
  7694. @ Here is a procedure for ``outputting'' a |dvi| sequence by storing it in
  7695. |vf|; each command is output in the minimum number of bytes.
  7696.  
  7697. We cheat and abuse VAX-\PASCAL's variant records to provide a simple
  7698. conversion between integers (signed or unsigned) and bytes in bigendian
  7699. order.  Variable |byte_ct| determines how many bytes are actually required.
  7700.  
  7701. Parameter |code| is the command code being output; this will be incremented
  7702. (by |1..3|) if |2..4| parameter bytes are necessary.  |param| is obviously
  7703. the parameter for |code|; |signed| is negative if |param| is to be
  7704. interpreted in twos-complement notation.  Otherwise, |signed=0|, except for
  7705. |set_rule| and |put_rule| (which require that all four bytes are output for
  7706. the parameter) when |signed=1|.  For output of the second parameter of these
  7707. latter two commands, |code=0|, which suppresses output of the |code| byte.
  7708.  
  7709. The following definition is used for any bytes which must be copied to array
  7710. |vf|.
  7711.  
  7712. @d vf_store(#)==begin vf[vf_ptr]:=#; incr(vf_ptr) end
  7713.  
  7714. @<Declare |read_VF|'s local procedures@>=
  7715. procedure vf_to_dvi(@!code:eight_bits;@!param:integer;@!signed:integer);
  7716.   var @!byte_ct:0..3; {offset to be added to |code|}
  7717.       @!cheat:packed record
  7718.                 case boolean of
  7719.                   false: (b0,b1,b2,b3:eight_bits);
  7720.                   true:  (fullword:integer)
  7721.               end;
  7722. begin
  7723.   cheat.fullword:=param; byte_ct:=3;    {assume needs 4-byte parameter}
  7724.   if signed<=0 then
  7725.   with cheat do
  7726.   if ((b3=0) and ((signed=0) or (b2<128)))
  7727.     or ((b3=255) and (signed<0) and (b2>=128)) then
  7728.   begin
  7729.     byte_ct:=2; {if |b3| superfluous, store 3-byte parameter}
  7730.     if ((b2=0) and ((signed=0) or (b1<128)))
  7731.       or ((b2=255) and (signed<0) and (b1>=128)) then
  7732.     begin
  7733.       byte_ct:=1; {if |b3| and |b2| superfluous, store two bytes}
  7734.       if ((b1=0) and ((signed=0) or (b0<128)))
  7735.         or ((b1=255) and (signed<0) and (b0>=128)) then
  7736.         byte_ct:=0; {|b3|, |b2| and |b1| all superfluous; one byte parameter}
  7737.     end
  7738.   end;
  7739.   if code<>0 then
  7740.     vf_store(code+byte_ct); {store the appropriate command byte}
  7741.   if byte_ct>2 then vf_store(cheat.b3);
  7742.   if byte_ct>1 then vf_store(cheat.b2);
  7743.   if byte_ct>0 then vf_store(cheat.b1);
  7744.   vf_store(cheat.b0);
  7745. end;
  7746.  
  7747. @ This function computes how many bytes form the parameters of a command
  7748. taken from a character packet in the \.{VF} file; in the case of any special
  7749. sequences amongst these bytes (|xxx1..xxx4|), we read and discard all the
  7750. bytes here, since we don't support such sequences in virtual fonts.  The
  7751. number of bytes thus discarded is returned, negated.
  7752.  
  7753. Any occurrence of a command which is illegal in a character packet causes
  7754. the font to be rejected.
  7755.  
  7756. @<Declare |read_VF|'s local procedures@>=
  7757. function vf_skip( @!o:eight_bits) : integer;
  7758.   var @!temp : integer;
  7759. begin
  7760.   case o of
  7761.     sixty_four_cases(set_char_0),sixty_four_cases(set_char_0+64),
  7762.     w0,x0,y0,push,pop,sixty_four_cases(fnt_num_0):
  7763.       vf_skip:=0;
  7764.     set1,put1,fnt1,right1,w1,x1,down1,y1,z1:
  7765.       vf_skip:=1;
  7766.     set1+1,put1+1,fnt1+1,right1+1,w1+1,x1+1,down1+1,y1+1,z1+1:
  7767.       vf_skip:=2;
  7768.     set1+2,put1+2,fnt1+2,right1+2,w1+2,x1+2,down1+2,y1+2,z1+2:
  7769.       vf_skip:=3;
  7770.     set1+3,set_rule,put1+3,put_rule,right1+3,w1+3,x1+3,down1+3,
  7771.     y1+3,z1+3,fnt1+3:
  7772.       vf_skip:=4;
  7773.     xxx1: begin temp:=vf_one; vf_skip:=-1-temp @+end;
  7774.     xxx1+1: begin temp:=vf_two; vf_skip:=-2-temp @+end;
  7775.     xxx1+2: begin temp:=vf_three; vf_skip:=-3-temp @+end;
  7776.     xxx1+3: begin temp:=vf_quad; vf_skip:=-4-temp @+end;
  7777.     improper_DVI_for_VF:
  7778.       vf_abort('contains illegal command in character packet');
  7779. @:fatal error VF file contains illegal command}{\qquad\.{contains illegal command...}@>
  7780.   endcases;
  7781.   if (o>=xxx1) and (o-xxx1<4) then
  7782.   begin
  7783.     decr(vf_ptr); {``unstore'' the |xxx| command byte}
  7784.     while temp>0 do
  7785.     begin o:=vf_one; decr(temp) end  {discard |xxx| sequence bytes}
  7786.   end
  7787. end;
  7788.  
  7789. @ This procedure adds to the saved packet a command to select a font; this
  7790. will be an pseudo external font number, converted from the local font number
  7791. used when a physical font is mapped into the virtual one.  The procedure has
  7792. the side-effect of setting its second parameter to index the stored metrics
  7793. of the newly-selected font.
  7794.  
  7795. @<Declare |read_VF|'s local procedures@>=
  7796. procedure store_fnt(@!num:integer; var @!f:integer);
  7797. var@!k:0..max_fonts;
  7798. begin
  7799.   f:=-1; {this value allows us to detect lack of the font}
  7800.   for k:=0 to font_ptr-1 do {scan the local font reference numbers}
  7801.     if font_ref[k]=num then f:=k; {find its entry}
  7802.   if f<0 then
  7803.     vf_abort('packet ',c:1,'invokes unmapped font ',num:1);
  7804. @:fatal error VF file packet invokes}{\qquad\.{packet invokes unmapped font}@>
  7805.   num:=font_ext[f]; {convert to pseudo external number}
  7806.   f:=0; while font_num[f]<>num do incr(f);
  7807.   if num < 64 then vf_store(fnt_num_0+num) else
  7808.     vf_to_dvi(fnt1,num,0); {|fntN k[N]|}
  7809. end;
  7810.  
  7811. @ This procedure adds to the saved packet a command to set a character; it
  7812. is required because we have to read all the bytes of such a command (in
  7813. order to determine to what character a packet is being mapped).
  7814.  
  7815. @<Declare |read_VF|'s local procedures@>=
  7816. procedure store_char(@!cc:integer);
  7817. begin
  7818.   if cc < 128 then vf_store(set_char_0+cc) else
  7819.     vf_to_dvi(set1,cc,0); {|setN c[N]|}
  7820. end;
  7821.  
  7822. @ When a character packet is being read from the virtual font file, we utilize
  7823. this procedure to store the bytes, translating any font selection commands to
  7824. reference the pseudo external fonts that have been created corresponding to
  7825. each local font of the virtual font file.
  7826.  
  7827. The packet taken from the \.{VF} file is surrounded by a |push|/|pop| pair
  7828. to maintain the program's conception of the current position.   We start by
  7829. assuming local font $0$, which is that loaded as pseudo external font
  7830. |font_base|.
  7831.  
  7832. After the packet has been copied, an |eop| is inserted into the stored
  7833. packet to permit detection of the end when the packet is later ``played
  7834. back''.
  7835.  
  7836. @<Declare |read_VF|'s local procedures@>=
  7837. procedure vf_copy(@!pl,@!cc : integer);
  7838.   var @!f:-1..max_fonts; {index into physical font data structures}
  7839.       @!param_len:integer; {count of parameter bytes}
  7840.       @!local_font:integer; {index into local font data structures}
  7841.       @!mapped_char:integer; {index into a locally referenced font}
  7842.       @!command:eight_bits; {next |dvi| command in \.{VF} file}
  7843.       @!simple_packet, {packet contains only one |setN|, with maybe one |fntN|}
  7844.       @!one_set,@!one_fnt:boolean; {|false| if more than one of each}
  7845.       @!start_ptr,seq_ptr:0..vf_size; {pointers into bytes stored in |vf|}
  7846.       @!scaling_required:boolean; {dimension needs revision before storing}
  7847. begin
  7848.   if vf_ptr+pl>=vf_size then capacity_exceeded('too many virtual characters');
  7849. @:capacity exceeded too many virt}{\quad\.{too many virtual characters}@>
  7850.   start_ptr:=vf_ptr; vf_store(push);
  7851.   store_fnt(0,f);
  7852.   glyph_map(TeX_font)(cc).font_code:=f;
  7853.   one_set:=false; one_fnt:=false; simple_packet:=true; seq_ptr:=vf_ptr;
  7854.   while pl>0 do
  7855.   begin
  7856.     command:=vf_one;
  7857.     vf_store(command);
  7858.     decr(pl);
  7859.     param_len:=vf_skip(command);
  7860.     @<Act upon any font changing command@>;
  7861.     @<Act upon any character setting command@>;
  7862.     @<Act upon commands that require scaling of parameters@>@;
  7863.     case command of
  7864.       sixty_four_cases(set_char_0),sixty_four_cases(set_char_0+64),
  7865.       four_cases(set1): one_set:=true;
  7866.       sixty_four_cases(fnt_num_0),four_cases(fnt1): one_fnt:=true;
  7867.       set_rule, put_rule: {these have \\{two} 4-byte parameters}
  7868.         begin param_len:=param_len-4; simple_packet:=false
  7869.       end;
  7870.     othercases
  7871.       simple_packet:=false;
  7872.     endcases;
  7873.     if param_len<0 then {|abs(param_len)| bytes already copied}
  7874.       pl:=pl+param_len
  7875.     else
  7876.       while param_len>0 do {copy remaining bytes of |command|}
  7877.         begin command:=vf_one; vf_store(command);
  7878.           decr(pl); decr(param_len)
  7879.         end
  7880.   end;
  7881.   vf_store(pop);
  7882.   vf_store(eop);
  7883.   if simple_packet then vf_ptr:=start_ptr {discard the packet}
  7884.                    else with glyph_map(TeX_font)(cc) do
  7885.                    begin loaded:=virtual; {indicate where \AM\ how to find it}
  7886.                     seq_off:=start_ptr
  7887.                    end
  7888. end;
  7889.  
  7890. @ If a character packet contains any command to select a font, the command
  7891. must be expanded, and the local font number converted into a reference to
  7892. the appropriate pseudo external font.  Since this may have the effect of
  7893. reading some bytes from the packet, we reduce our goal by the appropriate
  7894. quantity.
  7895.  
  7896. If this is the first font changing command in the packet, the internal font
  7897. it defines is placed into the current character's |glyph_map| entry, to
  7898. permit later overwriting with the entry for the physical character.  If,
  7899. moreover, it is the first command \\{stored} in the packet, apart from the
  7900. initial |push| and the invocation of local font $0$, we can over-write the
  7901. latter.
  7902.  
  7903. @<Act upon any font changing command@>=
  7904. case command of
  7905.   sixty_four_cases(fnt_num_0): local_font:=command-fnt_num_0;
  7906.   fnt1: local_font:=vf_one;
  7907.   fnt1+1: local_font:=vf_two;
  7908.   fnt1+2: local_font:=vf_three;
  7909.   fnt1+3: local_font:=vf_quad;
  7910. othercases
  7911.   do_nothing
  7912. endcases;
  7913. if (command>=fnt_num_0) and (command<=fnt1+3) then
  7914. begin decr(vf_ptr); {``unstore'' the command byte}
  7915.   store_fnt(local_font,f);
  7916.   param_len := -param_len; {indicate bytes already copied}
  7917.   if vf_ptr=seq_ptr then {we can overwrite the `|fnt_num_0|'}
  7918.     vf_ptr:=start_ptr+1; {by writing immediately after the |push|}
  7919.   if not one_fnt then glyph_map(TeX_font)(cc).font_code:=f
  7920.                  else simple_packet:=false
  7921. end;
  7922.  
  7923. @ Character packets usually contain one or more commands to set a character;
  7924. this code reads all necessary bytes to determine the code for the character
  7925. being set.  Note that this code is \&{not} activated for commands to \\{put}
  7926. characters, since these usually appear only in more complex character
  7927. packets.  If no previous character setting command has been met, then the
  7928. character code is written into the |glyph_map|.
  7929.  
  7930. @<Act upon any character setting command@>=
  7931. case command of
  7932.   sixty_four_cases(set_char_0),sixty_four_cases(set_char_0+64):
  7933.     mapped_char:=command-set_char_0;
  7934.   set1: mapped_char:=vf_one;
  7935.   set1+1: mapped_char:=vf_two;
  7936.   set1+2: mapped_char:=vf_three;
  7937.   set1+3: mapped_char:=vf_quad;
  7938. othercases
  7939.   do_nothing
  7940. endcases;
  7941. if (command>=set_char_0) and (command<=set1+3) then
  7942. begin decr(vf_ptr); {``unstore'' the command byte previously copied}
  7943.   store_char(mapped_char); {store a new character setting sequence}
  7944.   param_len:=-param_len; {indicate this many bytes already consumed}
  7945.   with glyph_map(f)(mapped_char) do
  7946.     if loaded=unused then loaded:=wanted;
  7947.   if not one_set then glyph_map(TeX_font)(cc).char_code:=mapped_char
  7948.                  else simple_packet:=false;
  7949.   if font_type[f]=unused then font_type[f]:=wanted;
  7950. end;
  7951.  
  7952. @ Many other |dvi| commands within a character packet require that the
  7953. parameters be read, because they must be scaled by the size at which the
  7954. virtual font is used.  We start by assuming that the |dvi| bytes \&{will} be
  7955. read from the \.{VF} file, and thus negate |param_len| so that the main loop
  7956. will know that those bytes have been read.  If we \&{don't} read the
  7957. command's parameters, |param_len| will be negated back again, and thus lead
  7958. to that number of bytes being copied verbatim from the \.{VF} file to |vf|.
  7959.  
  7960. @<Act upon commands that require scaling of parameters@>==
  7961. param_len:=-param_len;
  7962. scaling_required:=false;
  7963. case command of
  7964. set_rule,put_rule:
  7965.   begin q:=vf_quad;  {read height of rule}
  7966.     decr(vf_ptr); {``unstore'' command byte}
  7967.     @<Determine actual scaling for physical font@>;
  7968.     vf_to_dvi(command-3,q,1);   {store command and first parameter}
  7969.     q:=vf_quad; {read width of rule}
  7970.     param_len:=-8; {we've consumed 8 bytes from |pl|}
  7971.     @<Determine actual scaling for physical font@>;
  7972.     vf_to_dvi(0,q,1) {store second parameter}
  7973.   end;
  7974. right1,w1,x1,down1,y1,z1:
  7975.   begin q:=vf_byte; scaling_required:=true end;
  7976. right1+1,w1+1,x1+1,down1+1,y1+1,z1+1:
  7977.   begin q:=vf_pair; scaling_required:=true end;
  7978. right1+2,w1+2,x1+2,down1+2,y1+2,z1+2:
  7979.   begin q:=vf_trio; scaling_required:=true end;
  7980. right1+3,w1+3,x1+3,down1+3,y1+3,z1+3:
  7981.   begin q:=vf_quad; scaling_required:=true end;
  7982. othercases
  7983.   param_len:=-param_len
  7984. endcases;
  7985. if scaling_required then
  7986. begin
  7987.     decr(vf_ptr); {``unstore'' the command byte}
  7988.     @<Determine actual scaling for physical font@>;
  7989.     vf_to_dvi(command+param_len+1,q,-1) {store command again, with revised dimension}
  7990. end;
  7991.  
  7992. @ When a character packet is found, we only bother with it if it defines a
  7993. character that's used in the document being processed; other character
  7994. packets are merely skipped.
  7995.  
  7996. If the packet consists merely of commands to set a single character from one
  7997. of the physical fonts to which the virtual font is mapped, then we can
  7998. simply alter the information in the |glyph_map| to redirect requests to the
  7999. appropriate character of the physical font; the |glyph_map| of that font is
  8000. in turn marked to indicate that the character is used in the current
  8001. document.
  8002.  
  8003. On the other hand, if the character packet requires more complex positioning
  8004. commands, the whole packet is stored in array |vf|; the starting position of
  8005. the sequence is recorded in field |seq_off| of the |glyph_map| entry, whilst
  8006. the |loaded| field is given the value |virtual|.  Field |font_code| will
  8007. contain the internal font number assigned to local font zero of the virtual
  8008. font, whereas the font selection commands stored in |vf| use the pseudo
  8009. external font numbers, since these bytes will require interpretation by the
  8010. normal dvi-reading routines.
  8011.  
  8012. @<Other values of |download_status|@>==,@!virtual
  8013.  
  8014. @ @<Other fields of |glyph_info|@>==@!seq_off : integer;
  8015.  
  8016. @ The \.{VF} format supports arbitrary 4-byte character codes, but \.{VPL}
  8017. format presently does not.
  8018.  
  8019. Since the |tfm| widths in the character packets are unscaled, it would be
  8020. difficult to ensure that they correspond with those determined when the
  8021. virtual font's metrics were read, so they will be ignored at this time.
  8022.  
  8023. @d nonexistent(#)==((#<font_bc[TeX_font])or(#>font_ec[TeX_font])or
  8024.                     (char_width(TeX_font)(#)=invalid_width))
  8025.  
  8026. @<Read and store a character packet@>=
  8027. begin if temp_byte=long_char then
  8028.   begin pl:=vf_quad; c:=vf_quad; temp_int:=vf_quad;
  8029.     {|pl[4]| |cc[4]| |tfm[4]|}
  8030.   end
  8031. else begin pl:=temp_byte; c:=vf_one; temp_int:=vf_trio;
  8032.     {|pl[1]| |cc[1]| |tfm[3]|}
  8033.   end;
  8034. if nonexistent(c) then vf_abort('Character ',c:1,' does not exist!');
  8035. @:fatal error VF file Character c does not exist}{\qquad\.{Character does not exist}@>
  8036. if packet_start[c]<vf_size then
  8037.   warning('Discarding earlier packet for character ',c:1);
  8038. @:Warning: Discarding earlier packet}{\quad\.{Discarding earlier packet...}@>
  8039. if pl<0 then vf_abort('Negative packet length!');
  8040. @:fatal error VF file Negative packet length}{\qquad\.{Negative packet length}@>
  8041. packet_start[c]:=vf_ptr;
  8042. if glyph_map(TeX_font)(c).loaded <> unused then
  8043.   vf_copy(pl,c)
  8044. else
  8045.   while pl>0 do {discard unwanted character packet}
  8046.   begin temp_byte:=vf_one;
  8047.     decr(pl)
  8048.   end;
  8049. packet_found:=true;
  8050. end
  8051.  
  8052. @ Once all characters from physical fonts have been downloaded (and hence
  8053. may be assumed to have been mapped to character positions in the LN03's
  8054. downloaded fonts), we can complete the |glyph_map| entries for any virtual
  8055. fonts that may be present; for simple characters, this just involves copying
  8056. the entry for the appropriate character in the physical font.
  8057.  
  8058. We start with the most recently loaded font, so that chains through a number
  8059. of virtual fonts will be completed correctly.
  8060.  
  8061. @<Complete mapping of virtual characters@>=
  8062. for TeX_font:=nf-1 downto 0 do
  8063.   if font_type[TeX_font]=virtual then {only process virtual fonts}
  8064.   begin
  8065.     for m:=first_glyph_index(TeX_font) to last_glyph_index(TeX_font) do
  8066.     {cover all possible characters}
  8067.       with glyphs[m] do
  8068.         if loaded=wanted then
  8069.           glyphs[m]:=glyph_map(font_code)(char_code)
  8070.         else if loaded=virtual then {first char in sequence from this \TeX\ font}
  8071.           if font_type[font_code]=wanted then {provided it's a physical font}
  8072.             font_code:=txf_to_lnf[font_code] {maps to this LN03 font}
  8073.   end;
  8074.  
  8075.  
  8076. @*Description of LN03 Font Files.  The font files downloaded conform to DEC
  8077. standard 180; the definitions given below have been derived by the author from
  8078. the description of this standard included in the ``Font File Format Manual'',
  8079. Version 1, Variant 0, available on request from DEC.
  8080.  
  8081. Although we refer herein to font `files' they actually are always considered
  8082. to reside in memory, either that available in the basic printer, or extra
  8083. memory provided by plug-in RAM cartridges; presumably the same format is also
  8084. used to encode the built-in fonts, and those available in plug-in ROM
  8085. cartridges.
  8086.  
  8087. A font file is divided into ten regions; all data items may be referenced
  8088. \\{via} their offsets within one of these regions, which are predeterminable
  8089. for any particular combination of characters included in the file.  All
  8090. multi-byte quantities in a font file are stored in Vax/VMS standard form, with
  8091. the least-significant byte being at the \&{lower} address; this is the
  8092. opposite to the normal `Bigendian' order of most \TeX ware.
  8093.  
  8094. Since it is necessary to build the entire font file in memory before it can be
  8095. included in the \.{LN3} file for downloading to
  8096. the printer, we allocate a large array of memory sufficiently large to store
  8097. the rasters for the largest imaginable font file; the definition of
  8098. |lnf_bufsize| probably exceeds the maximum amount of memory available on the
  8099. printer by a comfortable margin!  This program refers to this `file' in memory
  8100. as the \.{LNF} file, although it is never written to a file as such; if this
  8101. program is adapted for use on a computer without virtual memory, it might
  8102. @^system dependencies@>
  8103. be necessary to utilize a random-access file to build the |lnf_file|.
  8104.  
  8105. Since the partially-built LN03 font file must be retained between successive
  8106. calls of |add_txf_to_lnf|, its data structures need to be declared globally.
  8107. Information in this ``file'' can consist of single bytes, unsigned (two-byte)
  8108. words, and signed (four-byte) longwords, as well as four-character strings, so
  8109. we define an |lnf_block| as a \PASCAL\ variant record so that this may be
  8110. (ab)used to permit the different methods of access.
  8111.  
  8112. We also declare here the |pxl_dir_type| which describes the 16-byte directory
  8113. record in a \.{PXL} file for any character's glyph.
  8114.  
  8115. @<Types...@>=
  8116. type @!lnf_block=packed record
  8117.         case integer of
  8118.         0: (@!bytes:packed array[0..511] of eight_bits);
  8119.         1: (@!words:packed array[0..255] of sixteen_bits);
  8120.         2: (@!longs:packed array [0..127] of integer);
  8121.         3: (@!str:packed array [0..127] of packed array [1..4] of char)
  8122.      end;
  8123.  
  8124. @ Many variables used by |add_txf_to_lnf| are declared globally, so that the
  8125. information contained therein is preserved between successive calls of the
  8126. function. |lnf| is the large memory-held `file' referred to above.
  8127.  
  8128. The variables |psize|, |lsize| and |msize| accumulate counts of raster bytes
  8129. generated in portrait, landscape and mixed modes (this program only creates
  8130. portrait mode rasters).  |ras_beg| and |ras_len| are used to form the
  8131. descriptor pointing to the Character Definition Region, whilst
  8132. |lnf_pool_offset| points to the string pool.  |lnf_len| keeps track of the
  8133. total size of the `file', whilst |using_GR| is set true when the |GR|
  8134. (right-hand half) of the font is being generated. (The LN03 supports use of
  8135. the |GL| and |GR| characters sets, corresponding to characters in the
  8136. (hexadecimal) range |@"21..@"7E| and |@"A1..@"FE|, respectively.)
  8137.  
  8138. The variable |file_stat| receives the exit status from VAX-Pascal's file
  8139. handling routines.
  8140.  
  8141. @<Globals...@>=
  8142.   @!lnf: array[0..lnf_bufsize] of lnf_block;
  8143.   @!psize,@!lsize,@!msize: integer;
  8144.   @!ras_beg,@!ras_len: integer;
  8145.   @!lnf_pool_offset,@!lnf_len: integer;
  8146.   @!using_GR: boolean;
  8147.   @!file_stat:integer;
  8148.  
  8149. @ The first region of an |lnf_file| is the file header.  This contains, as its
  8150. first longword (32-bit) quantity, a count of the total size of the file, in
  8151. bytes.  This is followed by the string |'FONT'|, to confirm that this is a DEC
  8152. Std 180 font.  This is followed immediately by a ``string descriptor''
  8153. pointing to the font-file identifier.  Font files contain many such descriptors;
  8154. each consists of two longwords, the first contains the string's length (in its
  8155. lower half), whilst the latter is the offset (relative to the start of the
  8156. file) of the first character of the string.
  8157.  
  8158. The font-file identifier descriptor is followed immediately (which is unusual)
  8159. by the characters of the identifier itself; for details of the content of this
  8160. string, read DEC Std 180.  The date and time of creation of the font follows;
  8161. this program arbitrarily sets these to 14:00:00 on 11-SEP-1973.
  8162.  
  8163. There then follow a number of descriptors pointing to the remaining regions of
  8164. the font file; these are, in order: |fnt_attr| font attributes region;
  8165. |fnt_params| font parameters region; |fnt_char_directory| font character
  8166. directory region; |fnt_seg_list| font segment list region; |fnt_future| font
  8167. future information region; |fnt_strings| font string pool region; |fnt_kerns|
  8168. font kerning information region; |fnt_chars| font character defintions region.
  8169.  
  8170. The header finishes with the character count information field, which contains
  8171. five longwords and three character locators (|fnt_first|$\to$|fnt_space|), the
  8172. organization flags, a count of the number of parameters used in each raster
  8173. definition and the raster expansion information field, which records the
  8174. number of characters defined in this file, and the number of bytes of rasters
  8175. required to define them; this field occupies a further 12 longwords
  8176. (|font_in_file_count|$\to$|fnt_mix_comp|).
  8177.  
  8178. @d fnt_total_size=0
  8179. @d fnt_identifier=fnt_total_size+4
  8180. @d fnt_version=fnt_identifier+4
  8181. @d fnt_file_id=@"14     {Where font identification appears}
  8182. @d fnt_date=@"58        {Where creation date starts}
  8183. @d fnt_attr=fnt_date+12 {Font attributes descriptor}
  8184. @d fnt_params=fnt_attr+8        {Font parameters descriptor}
  8185. @d fnt_char_directory=fnt_params+8      {Character directory descriptor}
  8186. @d fnt_seg_list=fnt_char_directory+8    {Font segment list descriptor}
  8187. @d fnt_future=fnt_seg_list+8    {Future info descriptor}
  8188. @d fnt_strings=fnt_future+8     {String pool descriptor}
  8189. @d fnt_kerns=fnt_strings+8      {Font kerning infor descriptor}
  8190. @d fnt_chars=fnt_kerns+8        {Character definitions descriptor}
  8191. @d fnt_first=fnt_chars+8        {First character in font}
  8192. @d fnt_last=fnt_first+4 {Last character in font}
  8193. @d fnt_less_than=fnt_last+4     {Locator for characters less then first}
  8194. @d fnt_greater_than=fnt_less_than+4     {Locator for characters greater than last}
  8195. @d fnt_error=fnt_greater_than+4 {Locator for the error character}
  8196. @d fnt_extension=fnt_error+4    {Extension count and use}
  8197. @d fnt_space=fnt_extension+8    {Space character code}
  8198. @d fnt_organisation=fnt_space+4 {Organisation flags}
  8199. @d fnt_no_of_params=fnt_organisation+4  {Number of character parameters}
  8200. @d fnt_in_file_count=fnt_no_of_params+4 {Number of locators in this file}
  8201. @d fnt_null_count=fnt_in_file_count+4   {Null locator count}
  8202. @d fnt_char_count=fnt_null_count+4      {Number of character definitions}
  8203. @d fnt_alt_blocks=fnt_char_count+4      {Number of alternate character blocks}
  8204. @d fnt_rasters=fnt_alt_blocks+4 {Number of rasters}
  8205. @d fnt_compressed=fnt_rasters+4 {Number of compressed rasters}
  8206. @d fnt_portrait=fnt_compressed+4        {Portrait mode byte count}
  8207. @d fnt_landscape=fnt_portrait+4 {Landscape byte count}
  8208. @d fnt_mixed=fnt_landscape+4    {``Mixed'' byte count}
  8209. @d fnt_port_comp=fnt_mixed+4    {Portrait compressed byte count}
  8210. @d fnt_land_comp=fnt_port_comp+4        {Landscape compressed byte count}
  8211. @d fnt_mix_comp=fnt_land_comp+4 {``Mixed'' compressed byte count}
  8212.  
  8213. @ The font attributes region always follows the header; items within this
  8214. region are referenced through their local offsets from the start of the
  8215. region.
  8216.  
  8217. The |fnt_attr_flags| carries 32 bits of font attribute information; these are
  8218. set arbitrarily by this program --- see the coding for details.
  8219.  
  8220. The character set designator (within the string pool) uniquely identifies the
  8221. character set contained within this font, and is accessed by the descriptor
  8222. |fnt_char_set|.  Similarly the |fnt_type_ID|, |fnt_type_family|, |fnt_font_ID|,
  8223. |fnt_type_cat| and |fnt_descr| descriptors access further strings in the
  8224. string pool.
  8225.  
  8226. Further fields in the font attributes region define numerical attributes of
  8227. the font (|fnt_type_size| thru |fnt_dev_char|).  The attributes region
  8228. concludes with a further two descriptors, pointing to strings giving the names
  8229. of the foundry and designer responsible for the font.
  8230.  
  8231. Numerical quantities referring to the dimensions of characters in DEC LN03
  8232. font files are commonly in ``points'' or ``centipoints'' (the latter obviously
  8233. being $1\over{100}$th of the former).  However, these are not true printer's
  8234. points (of which there are 72.27 to an inch); rather they are that horrible
  8235. compromise which imagines that there are exactly 72 points to an inch (and
  8236. which \TeX\ refers to as a \.{bp}).  Centipoints are also referred to as
  8237. `Gutenbergs', in tribute to the inventor of movable type.  It is quite
  8238. convenient for the LN03 (with 300 dots per inch) to work internally in
  8239. Gutenbergs, of which there are 7200 to an inch, since 24 Gutenbergs therefore
  8240. translate into a single pixel.
  8241.  
  8242. @d fnt_attributes=fnt_mix_comp+4        {Start of attributes region}@#
  8243.  
  8244. {Following define offsets within the Font Attributes Region of the ``file''}
  8245. @d fnt_attr_flags=0             {Flags e.g. roman}
  8246. @d fnt_char_set=fnt_attr_flags+4        {DESCR for character set designator}
  8247. @d fnt_designator_len=7         {Number of characters in set desginator string}
  8248. @d fnt_type_ID=fnt_char_set+8   {DESCR for Font Type Family ID}
  8249. @d fnt_type_ID_len=7            {Number of chars in Font Family ID string}
  8250. @d fnt_type_family=fnt_type_ID+8        {DESCR for Type Family Name}
  8251. @d fnt_type_family_len=16       {Number of chars in Type Family String}
  8252. @d fnt_font_ID=fnt_type_family+8        {DESCR for Font ID string}
  8253. @d fnt_font_ID_len=16           {Number of chars in Font ID string}
  8254. @d fnt_string_pool_len=fnt_designator_len+fnt_type_ID_len+fnt_type_family_len+fnt_font_ID_len
  8255. @d fnt_type_cat=fnt_font_ID+8   {DESCR for Type Category}
  8256. @d fnt_descr=fnt_type_cat+8     {DESCR for Font Description}
  8257. @d fnt_type_size=fnt_descr+8    {Font type size, in LN03 points}
  8258. @d fnt_average_width=fnt_type_size+4    {in centipoints}
  8259. @d fnt_resolution=fnt_average_width+4   {in $2^{-16}$ centipoints per pixel}
  8260. @d fnt_weight=fnt_resolution+4  {Coded 0..35. Normal is 16}
  8261. @d fnt_horiz_prop=fnt_weight+4  {0..35. Unexpanded/uncompressed is 16}
  8262. @d fnt_horiz_ratio=fnt_horiz_prop+4     {Ratio to a "normal" font}
  8263. @d fnt_pixel_aspect=fnt_horiz_ratio+4   {Ratio of X:Y of device's pixels}
  8264. @d fnt_rotation=fnt_pixel_aspect+4      {Ratio giving tangent of rotation}
  8265. @d fnt_dev_char=fnt_rotation+4  {Device specific characteristics}
  8266. @d fnt_foundry=fnt_dev_char+4   {DESCR of name of foundry}
  8267. @d fnt_designer=fnt_foundry+8   {DESCR of name of font designer}@#
  8268.  
  8269. @d fnt_attr_len=fnt_designer+8  {Length of Font Attributes Region of file}
  8270.  
  8271. @ The next region of the font file is the font parameters region.  All fields
  8272. of this region are referenced by means of their offsets within the region.
  8273. The first field is a 32-bit flags field, of which only two bits have defined
  8274. meanings.
  8275.  
  8276. The next field contains 8 items
  8277. (|fnt_underline_offset|$\to$|fnt_shadow_offset|) which define the position for
  8278. electronic over-, through- and under-lining, and the slant of the characters
  8279. and the delta offsets for emboldening.  None of these features are utilized by
  8280. DVItoLN03.
  8281.  
  8282. The next field (|fnt_superscript|$\to$|fnt_subscript|) describe the horizontal
  8283. and vertical offsets within the characters for the placement of super- and
  8284. sub-scripts.  The following field (|fnt_centreline|$\to$|fnt_digit_width|)
  8285. define various horizontal spacing parameters, whilst the final field of the
  8286. font parameters region (|fnt_topline_offset| thru |fnt_white_below|) define
  8287. vertical spacing parameters.
  8288.  
  8289. @d fnt_parameters=fnt_attributes+fnt_attr_len   {Start of Parameters Region}@#
  8290.  
  8291. {Following define offsets within the Font Parameters Region of the ``file''}
  8292. @d fnt_param_flags=0
  8293. @d fnt_underline_offset=fnt_param_flags+4       {All following in LN03 centipoints}
  8294. @d fnt_underline_thickness=fnt_underline_offset+4
  8295. @d fnt_thru_offset=fnt_underline_thickness+4    {Where strike-through line goes}
  8296. @d fnt_thru_thickness=fnt_thru_offset+4
  8297. @d fnt_overline_offset=fnt_thru_thickness+4
  8298. @d fnt_overline_thickness=fnt_overline_offset+4
  8299. @d fnt_slant=fnt_overline_thickness+4   {Ratio giving tan of slope from vertical}
  8300. @d fnt_shadow_offset=fnt_slant+4        {$\delta x$ and $\delta y$}
  8301. @d fnt_superscript=fnt_shadow_offset+4  {|y| and |x| offsets}
  8302. @d fnt_subscript=fnt_superscript+8
  8303. @d fnt_centreline=fnt_subscript+8       {Distance from left to centre widest char}
  8304. @d fnt_min_space=fnt_centreline+4       {Minimum acceptable width of interword space}
  8305. @d fnt_max_space=fnt_min_space+4
  8306. @d fnt_mean_space=fnt_max_space+4       {Width of space for monospace fonts}
  8307. @d fnt_em_width=fnt_mean_space+4        {Width of an |em| space}
  8308. @d fnt_en_width=fnt_em_width+4          {Width of an |en| space}
  8309. @d fnt_thin_space=fnt_en_width+4
  8310. @d fnt_digit_width=fnt_thin_space+4     {All digits usually same width}
  8311. @d fnt_topline_offset=fnt_digit_width+4 {From baseline to top of type size}
  8312. @d fnt_accent=fnt_topline_offset+4      {Where floating accents go}
  8313. @d fnt_halfline_offset=fnt_accent+4     {Where centre bar of an \.H appears}
  8314. @d fnt_overall_height=fnt_halfline_offset+4     {Sum of |height+depth|}
  8315. @d fnt_height=fnt_overall_height+4      {Includes ``leading'' above}
  8316. @d fnt_depth=fnt_height+4
  8317. @d fnt_H_height=fnt_depth+4
  8318. @d fnt_ex_height=fnt_H_height+4
  8319. @d fnt_white_above=fnt_ex_height+4
  8320. @d fnt_white_below=fnt_white_above+4@#
  8321.  
  8322. @d fnt_params_len=fnt_white_below+4     {Length of Font Parameters Region}
  8323.  
  8324. @ The next major region contains the character directory.
  8325.  
  8326. @d fnt_char_dir=fnt_parameters+fnt_params_len   {Where directory goes}
  8327.  
  8328. @ The next regions are: Table of Font Segments, Future Information, String
  8329. Pool and Kerning Information.  These are followed by the Character Definition
  8330. Region, which contains details of the rasters for each character's glyph.
  8331.  
  8332. Each character definition commences with |fnt_char_pars| of parameter
  8333. information; these are accessed through the offsets defined below.
  8334.  
  8335. In this version of the standard, each character is allocated 16 bytes of
  8336. parameter information.
  8337.  
  8338. @d fnt_char_pars=16     {Size of character parameters --- fixed}@#
  8339.  
  8340. @d fnt_kern_index=0             {Zero, or index into kerning table}
  8341. @d fnt_char_flags=fnt_kern_index+1      {Only 24 bits of flags}
  8342. @d fnt_char_width=fnt_char_flags+3      {In LN03 centipoints (or ``Gutenbergs'')}
  8343. @d fnt_left_bearing=fnt_char_width+4    {White space to left of raster}
  8344. @d fnt_raster_baseline=fnt_left_bearing+4  {Baseline relative to top of raster}
  8345.  
  8346. @ The character's parameters are followed by the encoded character raster:
  8347. this itself contains a number of counts preceding the rasters themselves; the
  8348. following definitions facilitate access to these fields.
  8349.  
  8350. @d fnt_char_orient=0            {0=Portrait, etc.}
  8351. @d fnt_char_type1=fnt_char_orient+1     {81H=Aligned expanded bit string}
  8352. @d fnt_char_type2=fnt_char_type1+1      {Unused if |fnt_char_type1| = 81H}
  8353. @d fnt_char_rows=fnt_char_orient+4      {Number of rows in raster}
  8354. @d fnt_char_cols=fnt_char_rows+2        {Number of columns in raster}
  8355. @d fnt_char_raster=fnt_char_cols+2      {Where rasters actually start}
  8356.  
  8357.  
  8358.  
  8359. @* Locating files containing character definitions. This procedure
  8360. determines the ``name'' of the required pixel file, and compares it with that
  8361. which is currently open, if any.  If these names differ, we close the currently
  8362. open file, if any, and open the new one. Although the file will have already
  8363. been opened and checked for validity during font file generation, we still
  8364. perform the checks, because that also sets certain pointers correctly.
  8365.  
  8366. This procedure is used by |print_glyph| to output a bitmap image of any
  8367. oversized rasters.  Note that it \&{has} parameter list |( TeX_font : integer)|;
  8368. this is omitted here because it has already been declared |forward|.
  8369.  
  8370. Note also that the variable |open_file_name| referenced is the \&{global}
  8371. variable of the same name, used by |print_glyph|, and not the identically
  8372. named formal parameter of |add_txf_to_lnf|.  The same name has to be used
  8373. because these \.{WEB} code sections incorporated are used in that function
  8374. also.
  8375.  
  8376. @p procedure change_pixel_file; {|change_pixel_file(TeX_font : integer);|}
  8377.   var @!new_file : file_spec;
  8378.     i: integer; {Used when reading the directory}
  8379. begin
  8380.   name_pxl_file(TeX_font,new_file,pxl_ident);
  8381.   if new_file<>open_file_name then
  8382.   begin
  8383.     if (open_file_name<>'') then
  8384.       close(pxl_file,@=disposition@>:=@=save@>,VAX_continue);
  8385.     open_file_name:=new_file;@/
  8386.     @<Open the file containing the glyph bitmaps@>;
  8387.    if pxl_ident=pixel_file then begin
  8388.        @<Check validity of PXL file@>;
  8389.        @<Read character directory from |pxl_file|@>
  8390.    end else begin
  8391.       @<Read preamble@>;
  8392.       skip_specials
  8393.    end
  8394.   end
  8395. end;
  8396.  
  8397. @* Creating the font file. The following function allocates the used
  8398. characters of the |TeX_font| to the predetermined locations in the |ln_font|;
  8399. the character rasters are taken from the LN03 font file |open_file_name|, which
  8400. has been set up to include the appropriate directory and file-name.  After each
  8401. font file has been constructed in memory, it is converted to sixel format (See
  8402. below) and added to the records in the \.{LN3} file |ln3_file|.
  8403.  
  8404. The use of each of the parameters and local variables is as follows:
  8405.  
  8406. \yskip\hang|ln_font| The serial number of the LN03 font being generated.
  8407. Should a font have to cross the boundaries between internal fonts, this will
  8408. be updated (it's not a |var| parameter, so won't upset anything in the
  8409. caller).  It is used in the procedure as a cross-check that the character
  8410. glyph currently being considered actually belongs to the font being created.
  8411.  
  8412.  
  8413. \yskip\hang|TeX_font| The identifying number of the \TeX\ font;
  8414. |font_num[TeX_font]| is that given by the \.{DVI} file.
  8415.  
  8416. \yskip\hang|open_file_name| Full file specification from which the rasters
  8417. should be read.
  8418.  
  8419. \yskip\hang|file_type| Indicates whether the |open_file_name| contains a
  8420. |packed_file| or a |pixel_file|.
  8421.  
  8422. \yskip\hang|rasters| Counts the number of bytes created in the current
  8423. |font_file|.  The variable is initialized to four times the number of
  8424. characters added since each character requires a four-byte character locator
  8425. in the font file directory region.
  8426.  
  8427. \yskip\hang|i| and |j| General loop counters.
  8428.  
  8429. Further local variables will be declared for the function as necessary.
  8430.  
  8431. @p function add_txf_to_lnf(ln_font,TeX_font:integer;
  8432.                 open_file_name:file_spec;
  8433.                 file_type:pixel_types):integer;
  8434. var  @<Local variables of |add_txf_to_lnf|@>@; {will be declared later}
  8435.   @!rasters:integer; {bytes added to font}
  8436.   @!i,@!j:integer; {general purpose pointers}
  8437.   @t\4@>@<Local procedures of |add_txf_to_lnf|@>@;
  8438. begin
  8439.   if file_type<>unavailable then
  8440.     @<Open the file containing the glyph bitmaps@>;
  8441.   @<Find name and magnification of font file@> ;
  8442.   case file_type of
  8443.   pixel_file:
  8444.     @<Prepare to use \.{PXL} file@>;
  8445.   packed_file:
  8446.     @<Prepare to use \.{PK} file@> ;
  8447.   unavailable:
  8448.     begin
  8449.       font_type[TeX_font]:=missing;
  8450.       open_file_name := '[unavailable]'
  8451.     end;
  8452.   endcases;
  8453.   max_ctr := font_ec[TeX_font];
  8454.   @<Report name of font file in use@> ;
  8455.   if file_type=unavailable then
  8456.     write(' unavailable; printing rules for glyphs;');
  8457.   print(' with '); write_ln(term_out,' loaded with ');
  8458.   @<Determine lowest and highest characters used in the \TeX\ font@>;
  8459.   rasters:=4*font_occupancy[TeX_font];
  8460.   @<Cycle through the characters of |TeX_font|, downloading as required@> ;
  8461.   @<Tidy up after copying rasters from \.{PXL} file@>;
  8462. end; {|add_txf_to_lnf|}
  8463.  
  8464. @ These variables used to be local to |add_txf_to_lnf|; however, some W{\mc
  8465. EB} modules invoked therein are now also used externally, but reference the
  8466. variables.  Therefore they are now global.
  8467.  
  8468. \yskip\hang|percent| Gives the magnification of the current font, for use in
  8469. font loading reports. (An unmagnified font is at 100\%.)
  8470.  
  8471. \yskip\hang|name| Holds the ``\TeX-oriented'' font name, for inclusion in
  8472. reports on the terminal, whilst the full file specification (from
  8473. |open_file_name|) is reported in the log file.
  8474.  
  8475. @<Glob...@>=
  8476. @!percent:integer;
  8477. @!name: file_spec;
  8478. @!name_ptr: 0..name_size; {pointer for indexing |name|}
  8479.  
  8480. @ As each font is downloaded, we want to report its name and its magnification
  8481. (if not \.{\BS magstep 0}) on the terminal, whilst the logfile will receive
  8482. the full file specification.  So we can commence by saving the name of the
  8483. font file, and the percentage magnification.
  8484.  
  8485. @<Find name and magnification of font file@>=
  8486. name := '';
  8487. for name_ptr:=font_name[TeX_font] to font_name[TeX_font+1]-1 do
  8488.   name := name + xchr[names[name_ptr]];  {ASCII representation of font name}
  8489. percent:=round((mag*0.1*font_scaled_size[TeX_font])/font_design_size[TeX_font]);
  8490.  
  8491. @ With rasters held in an unpacked pixel file, we need to check the file's
  8492. validity, and then transfer the directory information into |pxl_dir|.
  8493.  
  8494. @<Prepare to use \.{PXL} file@>=
  8495. begin
  8496.   @<Check validity of PXL file@>;
  8497.   @<Read character directory from |pxl_file|@>
  8498. end
  8499.  
  8500. @ On the other hand, with packed pixel files, we need to read through the
  8501. preamble and skip over any specials which precede the first character raster
  8502. in the file.
  8503.  
  8504. @<Prepare to use \.{PK} file@>=
  8505. begin
  8506.   @<Read preamble@>; skip_specials;
  8507.   if font_check_sum[TeX_font]<>checksum then
  8508.       warning('checksum doesn''t match in PK file')
  8509. @.checksum doesn't match in PK file@>
  8510. end
  8511.  
  8512. @ Once we've successfully opened the font file, we can report its name (and any
  8513. non-standard magnification).
  8514.  
  8515. @<Report name of font file in use@>=
  8516. print('Font ',name,' (at ',percent:1,'%) loaded from ',open_file_name);
  8517. write(term_out,'Font ',name);
  8518. if percent<> 100 then write(' (at ',percent:1,'%)');
  8519.  
  8520. @ We now set about creating (a portion of) an LN03 font file from the required
  8521. characters of |TeX_font|.  We assume at first that it will be possible to pack
  8522. the entire |TeX_font| into a single LN03 font, but may need to revise this
  8523. opinion if the font turns out to be longer than 188 characters.
  8524.  
  8525. A |while| control construct is used instead of the obvious |for| loop, because
  8526. there may be a necessity for premature termination of the loop.
  8527.  
  8528. @<Cycle through the characters of |TeX_font|, downloading as required@>=
  8529. char_ctr:=font_bc[TeX_font];
  8530. while (char_ctr<=max_ctr) do
  8531. begin
  8532.   @<Download character of font@>;
  8533.   incr(char_ctr);
  8534. end;
  8535. @<Verify that all glyphs were available@>;
  8536.  
  8537. @ For each character in the |TeX_font|, we need to see whether it has been
  8538. marked as being used, and, if so, whether it belongs to the |ln_font|
  8539. currently being created.
  8540.  
  8541. @<Download character of font@>=
  8542. with glyph_map(TeX_font)(char_ctr) do
  8543. if loaded<>unused then
  8544. begin
  8545.   @<Decide whereabouts to download the character@> ;
  8546.   @<Finish off any previous half font on LN03@> ;
  8547.   case file_type of
  8548.   packed_file:
  8549.     @<Copy glyph from packed file, and find the next one@>;
  8550.   pixel_file:
  8551.     copy_char(char_ctr,to_where);
  8552.   unavailable:
  8553.     @<Generate dummy directory entry for font that won't be used@>;
  8554.   endcases;
  8555. end
  8556.  
  8557. @ We need to decide whereabouts to put this character glyph in the LN03 font
  8558. file being created.  In the case of unpacked pixel files, we can simply use
  8559. the mapping determined at an earlier stage.  However, the glyphs in packed
  8560. pixel files appear in the same order as the original \MF\ source, so it is
  8561. necessary to download them to the LN03 in the order of arrival, otherwise we
  8562. may find ourselves jumping between the ``left-hand'' and ``right-hand'' parts
  8563. of the code table.
  8564.  
  8565. It is at this point that we update |first_txfc| to the next available slot in
  8566. the |ln_font|, if necessary, to skip over the non-existent characters between
  8567. |left_last| and |right_first|, or from |right_last| to the beginning of an
  8568. overflow font (if we're using packed files; unpacked ones, being restricted
  8569. to 128 characters, can never require an overflow).
  8570.  
  8571. @<Decide whereabouts to download the character@>=
  8572. if first_txfc=left_last+1 then
  8573.   first_txfc:=right_first
  8574. else if (first_txfc=right_last+1) and (file_type=packed_file) then
  8575.   @<Prepare for overflow portion, if necessary@>;
  8576. to_where:=first_txfc
  8577.  
  8578. @ If a font is completely unavailable (perhaps the user has requested it at
  8579. a magnification not provided on the current system), we still have to go
  8580. ahead and `complete' the font for the LN03, because the font file's headers
  8581. already reflect the previously determined mapping.  However, we shall
  8582. \\{never} image characters from the font, so we just make the character
  8583. locator entries in the font's ``directory'' zero.
  8584.  
  8585. @<Generate dummy directory entry for font that won't be used@>=
  8586. begin
  8587.   @<Create null character locator entry@>;
  8588.   loaded:=yes
  8589. end
  8590.  
  8591. @ Sometimes we find that we cannot complete the bitmap information relating
  8592. to a particular character, because the glyph is too large, or missing (or
  8593. the font itself is missing).  Since we will already have completed the LN03
  8594. font's description relating to number of characters, etc., we must still
  8595. provide an entry in the font directory; the LN03 will not attempt to image a
  8596. character for which the directory entry is zero, so that's what we put
  8597. there!
  8598.  
  8599. @<Create null character locator entry@>==
  8600. begin
  8601.   lnf_long(fnt_char_dir+4*(to_where-dir_base),0);
  8602.   decr(num_chars); incr(null_chars)
  8603. end
  8604.  
  8605.  
  8606. @ Whenever we first meet a character taken from the ``right-hand'' half of the
  8607. LN03 font (the set designated as GR, in ISO2022/ANSI X3.64 parlance), we need
  8608. to finish off the half font which has been created in the left-hand table.
  8609. Similarly, if we meet a character from the ``left-hand'' half (GL) when we had
  8610. previously been adding characters to the GR set, it implies that we are
  8611. commencing output, to GL, of characters taken from a \TeX\ font which was too
  8612. large to be loaded into the 188 characters of the previous |ln_font|.
  8613.  
  8614. The font file to date is written out and a new one initialized to hold the GR
  8615. character set.  Such successive font files are separated from each other by a
  8616. simple `\.,' character in the \.{LN3} file.
  8617.  
  8618. @<Finish off any previous half font on LN03@>=
  8619. if ((not using_GR) and (to_where>=right_first)) or
  8620.    (using_GR and (to_where<=left_last)) then
  8621. begin output_LNF;
  8622.   write_ln(ln3_file,',');@/
  8623.   using_GR:=not using_GR;
  8624.   init_LNF
  8625. end
  8626.  
  8627. @ If glyphs are being taken from a file of packed pixel rasters, we need to
  8628. copy the glyph to the LN03 file as the raster is built up from the file.
  8629. Having thus disposed of this glyph, we then scan through the file until
  8630. finding the \&{next} wanted glyph.
  8631.  
  8632. @<Copy glyph from packed file, and find the next one@>=
  8633. begin
  8634.   repeat
  8635.     @<Unpack and write...@>;
  8636.     skip_specials
  8637.   until (glyph_map(TeX_font)(char_num).loaded<>unused) or
  8638.         (flag_byte=pk_post);
  8639.   with glyph_map(TeX_font)(char_num) do
  8640.   if loaded<>unused then
  8641.   begin
  8642.     char_code:=first_txfc;
  8643.     font_code:=ln_font;
  8644.     incr(first_txfc)    {This might later be corrected}
  8645.   end;
  8646.   if flag_byte=pk_post then char_ctr := 255     {Break out}
  8647. end
  8648.  
  8649. @ Here are some local variables of |add_txf_to_lnf|, used in the above code:
  8650.  
  8651. \yskip\hang|char_ctr| Used to cycle through all possible character codes in
  8652. the |TeX_font|.
  8653.  
  8654. \yskip\hang|max_ctr| Unpacked pixel files (by their very architecture) can
  8655. contain a maximum of 128 glyphs; packed files (in this implementation) may
  8656. contain up to 256.  This variable permits more efficient operation by limiting
  8657. the values assumed by |char_ctr|.
  8658.  
  8659. @<Local variables of |add_txf_to_lnf|@>==
  8660. @!char_ctr:integer;
  8661. @!max_ctr:integer;
  8662.  
  8663.  
  8664. @ After having downloaded the last character of |TeX_font| which belongs in
  8665. |ln_font|, we arrange things so that any further characters will be loaded into
  8666. |ln_font+1|.   We accordingly want to start at |left_first| (which ought to
  8667. be the value held in |txfc_next|), and ensure that the font is written out to
  8668. the GR part of |ln_font|, before incrementing to the next font.
  8669.  
  8670. This code can only be invoked when working with packed pixel files, because
  8671. fonts which run over more than one LN03 font must exceed 188 characters in
  8672. length, and unpacked pixel files are limited to 128 characters!
  8673.  
  8674. @<Prepare for overflow portion, if necessary@>=
  8675. begin
  8676.   incr(ln_font);
  8677.   last_txfc := final_txfc;    {|first_txfc>right_last|, therefore\dots}
  8678.   @<Are we starting or finishing an LN03 font ``file''?@>; {|begin_txf:=false|}
  8679.   first_txfc := txfc_next     {This will be our next character}
  8680. end
  8681.  
  8682.  
  8683. @ The first job |add_txf_to_lnf| has to do is to open the (packed or
  8684. unpacked) pixel file; the file directory and font name are passed in to the
  8685. function (as parameter |open_file_name|).
  8686.  
  8687. Note that this \.{WEB} section is expanded by \.{TANGLE} in the context of
  8688. procedure |change_pixel_file| \&{and} of function |add_txf_to_lnf|.  In the
  8689. first context, the string variable |open_file_name| referenced herein is the
  8690. \&{global} variable of that name, whilst within |add_txf_to_lnf| it is the
  8691. formal parameter of that function.
  8692.  
  8693. Assuming the file is opened succesfully, the size of the file is taken from
  8694. variable |file_len|, which is set up by the \.{user\_action} procedure with
  8695. which the file is opened.
  8696.  
  8697. The first block of the file is then read (into the associated file variable
  8698. |pxl_file|).
  8699.  
  8700. @<Open the file containing the glyph bitmaps@>=
  8701. begin open(pxl_file,open_file_name,@=readonly@>,@=access_method@>:=@=direct@>,
  8702.       @=user_action@>:=file_open,VAX_continue);
  8703. file_stat:=status(pxl_file); ask:=file_stat<>0;
  8704. if ask then
  8705. begin
  8706.   case file_stat of
  8707.   2: file_message := 'PAS-E-ERRDUROPE';
  8708.   3: file_message := 'PAS-E-FILNOTFOU';
  8709.   5: file_message := 'PAS-E-ACCMETINC';
  8710.   6: file_message := 'PAS-E-RECLENINC';
  8711.   7: file_message := 'PAS-E-RECTYPINC';
  8712.   8: file_message := 'PAS-E-ORDSPEINC';
  8713.   othercases
  8714.      file_message := str_int(file_stat);
  8715.   endcases;
  8716.   abort('Couldn''t open ',open_file_name,'. Status=',file_message);
  8717. @:fatal error Couldn't open file}{\quad\.{Couldn't open file} (pxl/pk)@>
  8718. end;
  8719. reset(pxl_file)
  8720. end
  8721.  
  8722. @ We apply some simple checks to determine whether the \.{PXL} file passed in
  8723. has a valid format.  At the same time, we set up the local variable |pxl_size|
  8724. which gives the total length of the \.{PXL} file, in bytes, excluding the
  8725. identification longword of 1001 at its end.
  8726.  
  8727. @<Check validity...@>=
  8728. pxl_size:=file_len;
  8729. pxl_block:=0;
  8730. move_to_pxl(pxl_size-4);
  8731. i:=pxl_ptr+4;           {Point {\bf after} the last block}
  8732. repeat
  8733.   i:=i-4
  8734. until (i<0) or (pxl_long(i)=pxl_id);
  8735. pxl_size:=i+VAX_block_length*pxl_block;
  8736. if i<0 then abort('PXL file has bad format');
  8737. @:fatal error PXL file has bad format}{\quad\.{PXL file has bad format}@>
  8738.  
  8739. @ Having found the end of the \.{PXL} file, we are able to extract the
  8740. last four longwords.  These give us, starting with the last, the longword
  8741. address of the start of the character directory within the file, the ``design
  8742. size'' (in scaled points), the ``magnification'' of the font (will be 1500
  8743. for fonts designed for 300 pixels/inch devices) and the font checksum.
  8744. This latter quantity should match that read from the \.{TFM} file by \TeX.
  8745.  
  8746. After using these longwords, we then go to the first byte of the character
  8747. directory, and copy it into |pxl_dir|.  Each character's entry consists
  8748. of the following fields:
  8749. \yskip\hang|num_cols| the number of pixels printed for each row of the
  8750. character;
  8751. \yskip\hang|num_rows| number of rows of pixels printed;
  8752. \yskip\hang|x_off| offset (in pixels) of the left-hand edge of the raster,
  8753. relative to the reference point;
  8754. \yskip\hang|y_off| similarly for top of raster relative to the baseline;
  8755. \yskip\hang|addr| the longword address within the \.{PXL} file of the start
  8756. of the rasters;
  8757. \yskip\hang|tfm_width| the character's width, in the same format as for
  8758. |tfm_file|.
  8759.  
  8760. @<Read character directory from |pxl_file|@>=
  8761.  
  8762. move_to_pxl(pxl_size-16);
  8763. if font_check_sum[TeX_font]<>long_pxl(pxl_ptr) then
  8764.   warning('checksum doesn''t match in PXL file');
  8765. @.checksum doesn't match in PXL file@>
  8766. magnification:=long_pxl(pxl_ptr);
  8767. dsize:=long_pxl(pxl_ptr);
  8768. dsize:=(dsize/two_to_the_20th)*(magnification/1500.0);@/
  8769. move_to_pxl(long_pxl(pxl_ptr)*4);               {Go to start of directory}
  8770. for i:=0 to 127 do
  8771.   with pxl_dir[i] do
  8772.   begin num_cols:=word_pxl(pxl_ptr);
  8773.     num_rows:=word_pxl(pxl_ptr);
  8774.     x_off:=sign_pxl_word(pxl_ptr);
  8775.     y_off:=sign_pxl_word(pxl_ptr);
  8776.     addr:=long_pxl(pxl_ptr);
  8777.     tfm_width:=long_pxl(pxl_ptr)
  8778.   end
  8779.  
  8780. @ We need to determine the lowest and highest ordinal character positions of
  8781. those characters used in the \TeX\ font being encoded into the LN03 font file;
  8782. the variables |first_txfc| and |last_txfc| are used for this purpose.  If there
  8783. are no characters used at all in the \TeX\ font some malfunction must have
  8784. occurred, because |add_txf_to_lnf| should not have been invoked under those
  8785. circumstances.
  8786.  
  8787. The character numbers will be those of the LN03 font file, having already been
  8788. mapped to these during the mapping phase; therefore, if the lowest character
  8789. number used is |= left_first| then we must be starting a new font, so must
  8790. initialize the empty font file and indicate that this is a |GL| font
  8791. (left-hand half of eight-bit character table).
  8792.  
  8793. @<Determine lowest and highest characters used in the \TeX\ font@>=
  8794. first_txfc:=-1; last_txfc:=-1;      {For |ln_font|}
  8795. txfc_next:=-1; final_txfc:=-1;      {and for |ln_font+1|}
  8796. for i:=first_glyph_index(TeX_font) to last_glyph_index(TeX_font) do
  8797.   with glyphs[i] do
  8798.     @<Note |last_txfc|, and set |first_txfc| for first character@> ;
  8799. @<Are we starting or finishing an LN03 font ``file''?@>
  8800.  
  8801. @ As we cycle through all the allocated characters, we note each code in turn,
  8802. thus determining the highest code used.  We also note the lowest one when we
  8803. meet the first code used.  If the |TeX_font| extends into a second |ln_font|,
  8804. the corresponding information will be recorded in |final_txfc| and |txfc_next|,
  8805. respectively.
  8806.  
  8807. @<Note |last_txfc|, and set |first_txfc| for first character@>=
  8808. if loaded = wanted then
  8809. begin if txf_to_lnf[font_code]=ln_font then
  8810.   begin if first_txfc=-1 then first_txfc:=char_code;
  8811.     last_txfc:=char_code; font_code:=ln_font
  8812.   end
  8813.   else  {This must be overflow of |TeX_font| into |ln_font+1|}
  8814.   begin if txfc_next=-1 then txfc_next:=char_code;
  8815.     final_txfc:=char_code; font_code:=ln_font+1
  8816.   end
  8817. end
  8818.  
  8819. @ If the lowest character code used is |left_first|, then we shall have to
  8820. initialize a new LN03 font file.  Similarly, if the last character used is the
  8821. last character allocated to this LN03 font, we shall later have to finish off
  8822. the latter.  We therefore note these two conditions in |begin_txf| and
  8823. |end_txf|, and initialize the font ``file'' if the former condition pertains.
  8824.  
  8825. @<Are we starting or finishing an LN03 font ``file''?@>=
  8826. if last_txfc<0 then
  8827.       abort('Internal error (empty font)');
  8828. @:fatal error Internal error empty font}{\qquad\.{(empty font)}@>
  8829. begin_txf:=first_txfc=left_first;
  8830. end_txf:=last_txfc=last_lnf[ln_font];
  8831. if begin_txf then
  8832. begin using_GR:=false;
  8833.       init_LNF
  8834. end
  8835.  
  8836. @ Here are some more local variables of |add_txf_to_lnf|, used in the above
  8837. code:
  8838.  
  8839. \yskip\hang|first_txfc| The lowest character code allocated to a character of
  8840. |TeX_font| when the mapping took place.
  8841.  
  8842. \yskip\hang|last_txfc| Similarly, the highest code used.
  8843.  
  8844. \yskip\hang|txfc_next| The lowest character code for a character of |TeX_font|
  8845. allocated to |ln_font+1| when the mapping took place.
  8846.  
  8847. \yskip\hang|final_txfc| Similarly, the highest code used by |TeX_font| in
  8848. |ln_font+1|
  8849.  
  8850. \yskip\hang|begin_txf| A boolean, which is |true| if this |TeX_font| commences
  8851. a new font file in the LN03.
  8852.  
  8853. \yskip\hang|end_txf| This is |true| if the last character of the present
  8854. |TeX_font| is the last character mapped to |ln_font|.
  8855.  
  8856. @<Local variables of |add_txf_to_lnf|@>==
  8857. @!first_txfc, @!last_txfc : integer;
  8858. @!txfc_next, @!final_txfc : integer;
  8859. @!begin_txf, @!end_txf : boolean;
  8860. @ After all the required character glyphs have been copied from the \.{PXL}
  8861. file, we can close it.
  8862.  
  8863. If, and only if, the very last character code was that of the highest mapped
  8864. into this font, we can output the LN03 font to the \.{LN3} file.
  8865.  
  8866. Finally, we report the number of bytes of rasters created in the font file.
  8867.  
  8868. @<Tidy up after copying rasters from \.{PXL} file@>=
  8869. close(pxl_file,@=disposition@>:=@=save@>,VAX_continue);
  8870.  
  8871. if end_txf then output_LNF;
  8872.  
  8873. print_ln(rasters:0,' bytes of rasters (',font_occupancy[TeX_font]:1,
  8874.                           ' characters)');
  8875. write_ln(term_out,rasters:0,' bytes of rasters (',font_occupancy[TeX_font]:1,
  8876.                           ' characters)' ,crlf);
  8877. add_txf_to_lnf:=rasters {Total bytes of rasters added to file}
  8878.  
  8879. @* Procedures to access the font file. The definitions which follow facilitate
  8880. access to the individual bytes, words, longwords and strings of the
  8881. ``|lnf_file|'' by accessing the appropriate entity in the variant record
  8882. representation of the file's blocks. The procedures allow us to write bytes,
  8883. words and longwords into the memory representation of the |lnf_file|.
  8884.  
  8885. @d lnf_bytes(#)==lnf[(#) div VAX_block_length].bytes[(#) mod VAX_block_length]
  8886. @d lnf_words(#)==lnf[(#) div VAX_block_length].words[((#) mod VAX_block_length)
  8887. div 2]
  8888. @d lnf_longs(#)==lnf[(#) div VAX_block_length].longs[((#) mod VAX_block_length)
  8889. div 4]
  8890. @d lnf_str(#)==lnf[(#) div VAX_block_length].str[((#) mod VAX_block_length) div
  8891. 4]
  8892.  
  8893. @<Local procedures of |add_txf_to_lnf|@>=
  8894.  
  8895. procedure lnf_byte(byte_address:integer; byte_val:eight_bits);
  8896. begin
  8897.   lnf_bytes(byte_address):=byte_val
  8898. end;
  8899.  
  8900. procedure lnf_word(byte_address:integer; word_val:sixteen_bits);
  8901. begin if odd(byte_address) then
  8902.         abort('Internal error (word at odd byte ',byte_address:1,')');
  8903. @:fatal error Internal error word at odd byte}{\qquad\.{(word at odd byte)}@>
  8904.   lnf_words(byte_address):=word_val
  8905. end;
  8906.  
  8907. procedure lnf_long(byte_address,long_val:integer);
  8908. begin if odd(byte_address) then
  8909.         abort('Internal error (longword at odd byte ',byte_address:1,')');
  8910. @:fatal error Internal error longword at odd byte}{\qquad\.{(longword at odd byte)}@>
  8911.   if byte_address mod 4 = 0 then
  8912.     lnf_longs(byte_address):=long_val
  8913.   else
  8914.   begin if long_val<0 then
  8915.     begin long_val:=long_val+@'17777777777;     {$2^{31}-1$}
  8916.       long_val:=long_val+1;
  8917.       lnf_words(byte_address+2):=(long_val div 65536)+32768
  8918.     end else
  8919.       lnf_words(byte_address+2):=(long_val div 65536);
  8920.     lnf_words(byte_address):=long_val mod 65536
  8921.   end
  8922. end;
  8923.  
  8924. @ When we are ``outputting'' to the |lnf_file| from \.{PK} files, it's more
  8925. convenient to call the following procedure; in fact, it might prove handy for
  8926. use in |copy_char| too!
  8927.  
  8928. @<Additional low-level...@>=
  8929.  
  8930. procedure put_lnf(@!item : integer);
  8931. begin lnf_bytes(base):=item;
  8932.    incr(base)
  8933. end;
  8934. @#
  8935. procedure repeat_row;
  8936. begin put_lnf(lnf_bytes(row_addr));
  8937.    incr(row_addr)
  8938. end;
  8939.  
  8940. @ For downloading the bitmap of an oversized glyph, we need to ``read'' six
  8941. rows of pixels from either the |pxl_file| itself, or from the character's
  8942. bitmap which has been formed in |lnf_file| from the |packed_file|.
  8943.  
  8944. @<Read rows of pixels into sixel buffer@>=
  8945. if pxl_ident=packed_file then
  8946.   for j:=1 to ((num_cols+7) div 8) do
  8947.   begin
  8948.     sixel_line[k,j]:=reverse[lnf_bytes(base)];
  8949.     incr(base)
  8950.   end else
  8951.   begin
  8952.     for j:=1 to (num_cols+7) div 8 do
  8953.     begin
  8954.       sixel_line[k,j]:=pxl_byte(pxl_ptr);
  8955.       incr(pxl_ptr); must_get(pxl_ptr)
  8956.     end;
  8957.     while (pxl_ptr mod 4) <> 0 do
  8958.     begin
  8959.       incr(pxl_ptr); must_get(pxl_ptr)
  8960.     end
  8961.   end
  8962.  
  8963.  
  8964. @ It could happen that a glyph is imaged that doesn't exist in the font; we
  8965. then mark this in the |loaded| field of the |glyph_map| as |missing|; such
  8966. glyphs will result in appropriate $(x,y)$ positioning commands being output
  8967. to the LN03, by derivation from the font metrics data.
  8968.  
  8969. @<Other values of |download_status|@>= ,@!missing
  8970.  
  8971. @ After we've read through the (packed or unpacked) pixel file, we should have
  8972. managed to download glyphs for all the characters used in the current document.
  8973. If the following check finds a character without an associated glyph, it
  8974. downloads a dummy, invisible glyph of the appropriate width.
  8975.  
  8976. @<Verify that all glyphs were available@>=
  8977. for char_num := font_bc[TeX_font] to font_ec[TeX_font] do
  8978.   with glyph_map(TeX_font)(char_num) do
  8979.     if loaded=wanted then
  8980.     begin
  8981.       loaded := missing;    {Ensure we don't try to image the glyph}
  8982.       term_offset := 20;    {Ensure we get a newline before warning}
  8983.       warning('Font ',name,' doesn''t contain character ',char_num:1,crlf);
  8984. @:Warning: Font doesn't contain character}{\quad\.{Font doesn't contain...}@>
  8985.       @<Decide whereabouts to download the character@>;
  8986.       @<Finish off any previous half font on LN03@>;
  8987.       tfm_width := width;
  8988.       num_cols := pixel_width;
  8989.       base := ras_beg + ras_len;
  8990.       @<Create null character locator entry@>;
  8991.       char_code:=first_txfc; font_code:=ln_font
  8992.     end
  8993.  
  8994. @*Copy one character's rasters.  For each character taken from an unpacked
  8995. (\.{PXL}) file, the function |add_txf_to_lnf| calls this procedure |copy_char|,
  8996. which
  8997. provides the main action of the function, by converting the \.{PXL} file
  8998. rasters to the format required by the \.{LN03}, and storing them away in the
  8999. appropriate part of the ``|lnf_file|'' in memory.
  9000.  
  9001. The global variable |base| is used as a pointer into the appropriate byte of the
  9002. Character Definitions Region of the LN03 font file, and commences at the
  9003. location determined from the ``size so far'' as recorded by |ras_beg| and
  9004. |ras_len|.
  9005.  
  9006. Having verified that the \.{PXL} file contains the character glyph required
  9007. (if the character is absent, the address field of the directory entry will not
  9008. point to an address within the file), the |move_to_pxl| procedure is called to
  9009. make the appropriate bytes of the characters rasters accessible to the
  9010. program.
  9011.  
  9012. The character parameters section of the character definition is then completed
  9013. in the font file; the first four bytes contain the kerning information (null,
  9014. because \TeX\ does all the kerning for us, and we don't want the LN03 to upset
  9015. it) and 24 bits of flags --- the only bit that we set in this flag field is
  9016. the `flag flag' bit.
  9017.  
  9018. LN03 pixels (of which there are 300 to an inch) can be converted to DEC
  9019. centipoints (or Gutenbergs) by multiplying by 7200/300 = 24.
  9020.  
  9021. @d Pxl_to_Gut=24
  9022.  
  9023. @<Local procedures of |add_txf_to_lnf|@>=
  9024.  
  9025. procedure copy_char(char_num,to_where:integer);
  9026.   var @!i,@!j,@!k,@!n,@!len:integer;
  9027. begin
  9028.   base:=ras_beg+ras_len;        {Where to start saving the parameters/rasters}
  9029.   with pxl_dir[char_num] do
  9030.   if (addr>0) and (addr<=(pxl_size div 4)-8) then
  9031.   {Only process characters which lie within the file!}
  9032.   with glyph_map(TeX_font)(char_num) do
  9033.   begin
  9034.     move_to_pxl(addr*4);
  9035.     char_code:=to_where;@/
  9036.     @<Process |tfm_width| information@>;
  9037.     @<Validate size of character's glyph@>;
  9038.     @<Complete remainder of Character Definition parameters@>;
  9039.     @<Copy the rasters themselves@>;
  9040.     @<Calculate the number of bytes of rasters transferred@>;
  9041.     @<Complete the entry in the font character directory@>
  9042.   end {|with|}
  9043. end;
  9044.  
  9045.  
  9046. @ The true character width (in scaled points) is taken from the |tfm_width| field of
  9047. the pixel file directory; this value is converted to pixels and stored in the
  9048. |ch_widths| array, where it is used by the procedures which set characters on
  9049. the page.  The value is also converted to Gutenbergs and stored in the
  9050. |fnt_char_width| field for the character raster in the font file.  If the
  9051. width differs from that computed from the \.{TFM} file by more than one pixel,
  9052. this is reported, and the value derived from the \.{PXL} file substituted in
  9053. preference.
  9054.  
  9055.  
  9056. @<Process |tfm_width| information@>=
  9057. ch_wid:=round((dsize*resolution*tfm_width)/(two_to_the_20th*72.27));
  9058. lnf_word(base,@"0000);  {No kerning, lowest byte of flags=0}
  9059. lnf_word(base+2,@"8000);        {Highest bit of flags (flag flag) = 1}
  9060. lnf_long(base+fnt_char_width,trunc(ch_wid*(7200.0/resolution)));
  9061. ch_widths[ln_font,to_where]:=ch_wid;
  9062. tfm_wid:=pixel_width;
  9063. if abs(ch_wid-tfm_wid) > 1 then
  9064. begin warning('Pixel width discrepancy; font ',open_file_name,' char ',char_num:1);
  9065. @:Warning: Pixel width discrepancy}{\quad\.{Pixel width discrepancy}@>
  9066.   show('---computed width: ',tfm_wid:1,';  actual width: ',ch_wid:1);
  9067. end;
  9068. pixel_width:=ch_wid
  9069.  
  9070. @ The above section of code needs some workspace in which to perform those width
  9071. computations.  They may as well be local to |add_txf_to_lnf|.
  9072.  
  9073. \yskip\hang|ch_wid| Holds the actual width of the character, in pixels,
  9074. derived from the |tfm_width| field of the pixel file's directory.
  9075.  
  9076. \yskip\hang|tfm_wid| Holds the character width as calculated from the
  9077. |ch_widths| entry which came from the TFM file for this font.
  9078.  
  9079. @<Local variables of |add_txf_to_lnf|@>==
  9080. @!ch_wid,@!tfm_wid:integer;
  9081.  
  9082. @ The \.{PXL} file directory entry contains |x_off| and |y_off| fields, which
  9083. define the starting point of the raster relative to the character's reference
  9084. point.  These are passed to the font file as the |fnt_left_bearing| and
  9085. |fnt_raster_baseline| fields of the raster entry, in Gutenbergs.
  9086.  
  9087. The flags longword of the raster is then created, and indicates that the
  9088. raster is in portrait orientation (relative to memory, not to the output
  9089. device) and is an aligned expanded bit string.  (Unfortunately, although
  9090. DEC Std 180 permits run-length encoding of rasters, the LN03 itself does not
  9091. support this facility, so rasters have to be downloaded in fully expanded form.)
  9092.  
  9093. Invisible fonts (used by Sli\TeX) will have the bounding box info (|num_cols|
  9094. and |num_rows|) both equal to zero.  Since the LN03 isn't very happy with such
  9095. characters (!), we will later insert a null character locator in the font's
  9096. directory; we still go through the motions of generating character
  9097. parameters and the (empty) bitmap in the font file, but bytes occupied by
  9098. these will be overwritten with the next real bitmap.  Although instructions
  9099. to image such a null character are  accepted by the printer, unfortunately
  9100. the printer seems to image some other character at random!  Therefore we
  9101. mark the glyph as |missing|, so that no attempt is made to image it later.
  9102. Oversized glyphs are also not inserted into the font file; so it is only for
  9103. genuine printable glyphs that the value of |base| will be advanced to
  9104. reserve the space occupied by the character's parameters.
  9105.  
  9106. This module is always called in the context |with
  9107. glyph_map(TeX_font)(char_num) do|.
  9108.  
  9109. @<Complete remainder of Character Definition parameters@>=
  9110. lnf_long(base+fnt_left_bearing,int(-Pxl_to_Gut*x_off));
  9111. lnf_long(base+fnt_raster_baseline,int(-Pxl_to_Gut*y_off));
  9112. lnf_long(base+fnt_char_pars+fnt_char_orient,@"00008100);  {Aligned,bit-expanded}
  9113. if num_rows=0 then loaded := missing; {``Invisible'' glyph}
  9114. lnf_word(base+fnt_char_pars+fnt_char_cols,num_cols);
  9115. lnf_word(base+fnt_char_pars+fnt_char_rows,num_rows);
  9116. if loaded=yes then
  9117.   base:=base+fnt_char_pars+fnt_char_raster
  9118.  
  9119. @ Here are some further values than can be allocated to the |loaded| field
  9120. of |glyph_map|: values could be:\par
  9121. \item{$\bullet$} |yes| if character's glyph was included in a downloaded LN03
  9122. font;
  9123. \item{$\bullet$} |no| if the glyph exceeded the LN03's capability for the
  9124. area of a downloaded glyph.  In this case a null character locator will have
  9125. been inserted in the directory of the downloaded font, and the actual
  9126. glyph's bitmap will be sent to the printer each and every time it occurs in
  9127. the document.
  9128.  
  9129. @<Other values of |download_status|@>= ,@!yes ,@!no
  9130.  
  9131. @ The LN03 has a physical (apparently arbitrary) restriction on the maximum size
  9132. of a downloadable glyph.  For a glyph to be acceptable, it must satisfy the
  9133. inequality $$
  9134.   2\times\lceil|num_cols|/2\rceil\times\lceil|num_rows|/8\rceil\le5700$$
  9135.  
  9136. The following section prevents such an oversized glyph from being included in
  9137. the downloaded font; it is always called in the context |with
  9138. glyph_map(TeX_font)(char_num) do|.
  9139.  
  9140. @<Validate size of character's glyph@>=
  9141. if ((num_rows+7) div 8) * 2 * ((num_cols+1) div 2) > largest_glyph then
  9142.   loaded:=no  {Indicate oversized glyph unavailable}
  9143. else
  9144.   loaded:=yes {Indicate glyph successfully downloaded}
  9145.  
  9146.  
  9147. @ After further setting up of values in the font file, we copy the bits of the
  9148. rasters from the \.{PXL} file to the LN03 font file; this requires that we
  9149. reverse each byte.
  9150.  
  9151. As each byte is copied, we get another; the |must_get| procedure will read in
  9152. a new block from the \.{PXL} file if necessary.  At the end of each row, the
  9153. next is started from the next complete longword of the \.{PXL} file.
  9154.  
  9155. Only character glyphs which are capable of being downloaded are entered into
  9156. the file; invisible and oversized glyphs don't need to have the bitmap
  9157. copied, and they'll eventually end up with a null character locator in the
  9158. font file's directory.
  9159.  
  9160. This module is always called in the context |with
  9161. glyph_map(TeX_font)(char_num) do|.
  9162.  
  9163. @<Copy the rasters themselves@>=
  9164. if loaded=yes then
  9165. for i:=1 to num_rows do
  9166. begin
  9167.   for j:=1 to (num_cols+7) div 8 do             {Whole bytes required}
  9168.   begin lnf_bytes(base):=reverse[pxl_byte(pxl_ptr)];@/
  9169.     incr(pxl_ptr); must_get(pxl_ptr); incr(base)
  9170.   end;                  {Copied bytes for one row of pixels}
  9171.   while (pxl_ptr mod 4)<>0 do
  9172.   begin incr(pxl_ptr);  {Row held in 32-bit words in \.{PXL} file}
  9173.     must_get(pxl_ptr)
  9174.   end
  9175. end
  9176.  
  9177. @ The |copy_char| procedure requires an array to be set up to ``reverse''
  9178. individual bytes' bits, since the \.{PXL} file stores the first pixel of
  9179. a row at the most significant bit of the long-word, whilst DEC Std 180
  9180. requires the first pixel to be the least significant bit of the first byte.
  9181.  
  9182. We therefore create a global array |reverse|, which we initialize with
  9183. appropriate patterns to reverse any byte proferred as an index.
  9184.  
  9185. @<Globals...@>=
  9186. @!reverse: packed array [0..255] of eight_bits;
  9187.  
  9188. @ @<Procedures for...@>=
  9189. procedure Generate_Reversal;
  9190.   var @!index,@!patt,@!cnt,@!rev: eight_bits;
  9191. begin
  9192.   for index:=0 to 255 do
  9193.   begin rev:=0; patt:=index;
  9194.     for cnt:=1 to 8 do
  9195.     begin rev:=2*rev;
  9196.       if odd(patt) then rev:=rev+1;
  9197.       patt:=patt div 2
  9198.     end;
  9199.     reverse[index]:=rev
  9200.   end;
  9201. end;
  9202.  
  9203. @ @<Set initial...@>=
  9204.   Generate_Reversal;
  9205.  
  9206. @ The number of bytes required for the character glyph in landscape and
  9207. portrait modes is computed, and added to the accumulated sum of these for all
  9208. characters; the ``mixed'' mode count is increased by the larger of these
  9209. quantities.
  9210.  
  9211. The value |len| is also computed to be added to the Character Definitions
  9212. descriptor held in |ras_beg,ras_len|.  None of these quantities are updated
  9213. unless the character glyph has actually been copied into the font file,
  9214. i.e., |loaded=yes|.
  9215.  
  9216. @<Calculate the number of bytes of rasters transferred@>=
  9217. if loaded=yes then
  9218. begin
  9219. i:=((num_rows+7) div 8)*num_cols; {Bytes required for raster in landscape mode}
  9220. lsize:=lsize+i;
  9221. j:=((num_cols+7) div 8);                {Bytes/column}
  9222. psize:=psize+j*num_rows;        {bytes added to portrait rasters}
  9223. msize:=msize+VAX_max(i,j*num_rows); {bytes added to mixed mode rasters}
  9224. len:=j*num_rows+fnt_char_pars+fnt_char_raster   {Allow for the parameters}
  9225. end else len:=0
  9226.  
  9227. @ After transferring the rasters for one character's glyph, we finally
  9228. complete the entry in the font file's character directory.  We record the
  9229. address of the character's rasters in the appropriate character locator,
  9230. update the count of rasters stored, and round up to the next \\{word}
  9231. boundary in the font file (note that it is not a requirement of DEC Std 180
  9232. that rasters start on a \\{longword} boundary.)
  9233.  
  9234. If the glyph was not copied into the font file, because it exceeded the
  9235. LN03's maximum size of glyph, or because it was invisible, then a null entry
  9236. is made for the character locator, and the next raster, if any, will
  9237. overwrite that just stored.
  9238.  
  9239. @<Complete the entry in the font character directory@>=
  9240. if loaded<>yes then
  9241. begin
  9242.   lnf_long(fnt_char_dir+4*(to_where-dir_base),0);
  9243.   incr(null_chars); decr(num_chars)
  9244. end
  9245. else begin
  9246.   lnf_long(fnt_char_dir+4*(to_where-dir_base),ras_beg+ras_len);
  9247.   ras_len:=ras_len+len; rasters:=rasters+len;
  9248.   if odd(ras_len) then
  9249.   begin
  9250.     lnf_byte(base,0);           {Null byte to round raster to word boundary}
  9251.     incr(ras_len);
  9252.     incr(rasters)
  9253.   end
  9254. end
  9255.  
  9256. @ The following procedure initialises the ``fixed'' area of the \.{LN03} font
  9257. file as held in memory.  We start by clearing the whole of the first block of
  9258. the file; this is sufficient to ensure that the whole of the file header,
  9259. attributes and parameters regions are zeroed, which reduces the number of
  9260. individual assigments necessary.
  9261.  
  9262. The counts of space occupied by the rasters, and the raster sizes in portrait,
  9263. landscape and mixed modes are also initialized.
  9264.  
  9265. @<Local procedures of |add_txf_to_lnf|@>=
  9266. procedure init_LNF;
  9267. var @!i,@!j:integer;
  9268. begin
  9269.   for i:=0 to 127 do lnf_longs(4*i):=0; {Clear the lnf header}
  9270.   ras_len:=0; psize:=0; lsize:=0; msize:=0;@/
  9271.   @<Complete the font file's header region@>@;
  9272.   @<Complete the font file's attributes region@>@;
  9273.   @<Complete the font file's parameters region@>@;
  9274.   @<Complete the font file's string pool region@>@;
  9275. end;
  9276.  
  9277. @ The font file's header contains some identification material, and a number
  9278. of descriptors which point to other regions of the font file.
  9279.  
  9280. @<Complete the font file's header region@>=
  9281. lnf_str(fnt_identifier):='FONT';
  9282. lnf_long(fnt_version,1);        {Font File Format is V1.0}
  9283. file_ID:='U0000'+@=dec@>(ln_font,2,2)+'002SK00GG0001UZZZZ02F000';
  9284. if using_GR then file_ID[21]:='O';
  9285. lnf_long(12,VAX_length(file_ID)); lnf_long(16,fnt_file_id);
  9286. for j:=1 to VAX_length(file_ID) do lnf_byte(j+fnt_file_id-1,xord[file_ID[j]]);
  9287. {For the moment, we set creation date to 11-SEP-1973 14:00:00}
  9288. lnf_word(fnt_date,1973); lnf_word(fnt_date+2,9); lnf_word(fnt_date+4,11);
  9289. lnf_word(fnt_date+6,14); lnf_word(fnt_date+8,0); lnf_word(fnt_date+10,0);
  9290. lnf_long(fnt_attr,fnt_attr_len); lnf_long(fnt_attr+4,fnt_attributes);
  9291. lnf_long(fnt_params,fnt_params_len); lnf_long(fnt_params+4,fnt_parameters);
  9292. if using_GR then
  9293.   num_chars:=last_lnf[ln_font]-right_first+1
  9294. else
  9295.   if last_lnf[ln_font]<left_last then
  9296.     num_chars:=last_lnf[ln_font]-left_first+1
  9297.   else
  9298.     num_chars:=left_last-left_first+1;
  9299. lnf_long(fnt_char_directory,4*num_chars);
  9300. lnf_long(fnt_char_directory+4,fnt_char_dir);
  9301. lnf_pool_offset:=fnt_char_dir+4*num_chars;
  9302. lnf_long(fnt_seg_list,0); { Empty |seg_list| descriptor }
  9303. lnf_long(fnt_seg_list+4,0);
  9304. lnf_long(fnt_future,0); lnf_long(fnt_future+4,0);
  9305. lnf_long(fnt_strings,4*((fnt_string_pool_len+3) div 4));        {Rounded to longword boundary}
  9306. lnf_long(fnt_strings+4,lnf_pool_offset);
  9307. ras_beg:=lnf_pool_offset+4*((fnt_string_pool_len+3) div 4);
  9308. {We fill in address of Kerning Table, but leave it of length zero}
  9309. lnf_long(fnt_kerns+4,0);
  9310. lnf_long(fnt_chars+4,ras_beg);  {We can't fill in length until finished}
  9311. lnf_long(fnt_first,left_first); lnf_long(fnt_last,num_chars-1+left_first);
  9312. {No definitions (Yet!!!!) for less than, greater than and error characters}
  9313. {No usage of Extension field}
  9314. lnf_long(fnt_space," ");        {Space character value}
  9315. lnf_long(fnt_organisation,@"A8);
  9316.       {Expanded rasters;Identical Orientation; Large Values used}
  9317. lnf_long(fnt_no_of_params,fnt_char_pars);
  9318. lnf_long(fnt_in_file_count,num_chars);
  9319. null_chars:=0; lnf_long(fnt_null_count,null_chars); {Null count maybe revised later}
  9320. lnf_long(fnt_char_count,num_chars);
  9321. {Alternate blocks remains at zero}
  9322. lnf_long(fnt_rasters,num_chars);
  9323. {Compressed rasters remains at zero; psize, etc., are filled in at end}
  9324. if using_GR then dir_base:=right_first else dir_base:=left_first;
  9325. {Establish offset into font's character locators}
  9326.  
  9327. @ These used to be local variables of |add_txf_to_lnf|, used in the
  9328. previous section.  However, since we now require to use |num_chars| and
  9329. |null_chars| to update the font file before it is finally output, they have
  9330. to be global.  (Strictly speaking, |file_ID| could remain local, but that
  9331. would require much shuffling of the W{\mc EB}!)
  9332.  
  9333. \yskip\hang|file_ID| Holds the fixed-length string which identifies any
  9334. particular font file to the LN03.
  9335.  
  9336. \yskip\hang|num_chars| Gives the number of characters defined in each half of
  9337. the LN03 font file.
  9338.  
  9339. \yskip\hang|null_chars| Counts any null character locators added to the file
  9340. for oversized or missing glyphs.
  9341.  
  9342. \yskip\hang|dir_base| Code of the first character loaded in the current half
  9343. font; used to derive the correct offset into |font_char_dir| for completing
  9344. locators.
  9345.  
  9346. @<Global...@>=
  9347. @!file_ID:packed array [1..31] of char;
  9348. @!num_chars,@!null_chars:integer;
  9349. @!dir_base:integer;
  9350. @ The font file's attributes region contains mostly descriptors (of strings in
  9351. the string pool region) which identify the font, font family, \\{etc.}
  9352.  
  9353. @<Complete the font file's attributes region@>=
  9354. lnf_long(fnt_attributes+fnt_attr_flags,2);      {Roman}
  9355. lnf_long(fnt_attributes+fnt_char_set,fnt_designator_len);
  9356. lnf_long(fnt_attributes+fnt_char_set+4,lnf_pool_offset);
  9357. lnf_long(fnt_attributes+fnt_type_ID,fnt_type_ID_len);
  9358. lnf_long(fnt_attributes+fnt_type_ID+4,lnf_pool_offset+fnt_designator_len);
  9359. lnf_long(fnt_attributes+fnt_type_family,fnt_type_family_len);
  9360. lnf_long(fnt_attributes+fnt_type_family+4,
  9361.               lnf_pool_offset+fnt_designator_len+fnt_type_ID_len);
  9362. lnf_long(fnt_attributes+fnt_font_ID,fnt_font_ID_len);
  9363. lnf_long(fnt_attributes+fnt_font_ID+4,
  9364.       lnf_pool_offset+fnt_designator_len+fnt_type_ID_len+fnt_type_family_len);
  9365. {Type category, Font description descriptors empty}
  9366. {Following values are purely arbitrary at present; try harder later}
  9367. lnf_long(fnt_attributes+fnt_type_size,10);      {``10pt''}
  9368. lnf_long(fnt_attributes+fnt_average_width,500); {5.00pt}
  9369. lnf_word(fnt_attributes+fnt_resolution+2,24);   {24.0000 centipoints/pixel}
  9370. lnf_long(fnt_attributes+fnt_weight,16); {``Normal'' weight}
  9371. lnf_long(fnt_attributes+fnt_horiz_prop,16);     {Neither expanded nor compressed}
  9372. lnf_word(fnt_attributes+fnt_horiz_ratio,1);     {1:1 relative to a ``normal'' font}
  9373. lnf_word(fnt_attributes+fnt_horiz_ratio+2,1);
  9374. lnf_word(fnt_attributes+fnt_pixel_aspect,1);    {``Square'' pixels}
  9375. lnf_word(fnt_attributes+fnt_pixel_aspect+2,1);
  9376. lnf_word(fnt_attributes+fnt_rotation+2,1);      {tan = 0/1 = $0^\circ$}
  9377. {Device characteristics = 0}
  9378. {Foundry and Designer have empty descriptors}
  9379.  
  9380. @ The font file's parameters region contain information relating to all the
  9381. characters as a whole.  Since they relate to such features as electronic
  9382. underlining, which are not utilized by the \.{LN3} file generated by
  9383. DVItoLN03, the values used are purely arbitrary.  (The values are those used
  9384. by Flavio Rose's original \.{LN03toPP} (PL/I) program.)
  9385.  
  9386. @<Complete the font file's parameters region@>=
  9387. {Params flags = 0}
  9388. lnf_long(fnt_parameters+fnt_underline_offset,30);
  9389. lnf_long(fnt_parameters+fnt_underline_thickness,20);
  9390. lnf_long(fnt_parameters+fnt_thru_offset,-60);
  9391. lnf_long(fnt_parameters+fnt_thru_thickness,20);
  9392. lnf_long(fnt_parameters+fnt_overline_offset,-150);
  9393. lnf_long(fnt_parameters+fnt_overline_thickness,20);
  9394. lnf_word(fnt_parameters+fnt_slant+2,1); {tan = 0/1 = $0^\circ$}
  9395. lnf_word(fnt_parameters+fnt_shadow_offset+2,30);        {$\delta x$ only}
  9396. lnf_long(fnt_parameters+fnt_superscript,-90);           {$\delta y$ only}
  9397. lnf_long(fnt_parameters+fnt_subscript,40);              {$\delta y$ only}
  9398. lnf_long(fnt_parameters+fnt_centreline,60);
  9399. lnf_long(fnt_parameters+fnt_min_space,240);     {Note |min>max|!}
  9400. lnf_long(fnt_parameters+fnt_max_space,60);
  9401. lnf_long(fnt_parameters+fnt_mean_space,100);
  9402. lnf_long(fnt_parameters+fnt_em_width,240);
  9403. lnf_long(fnt_parameters+fnt_en_width,120);
  9404. lnf_long(fnt_parameters+fnt_thin_space,40);
  9405. lnf_long(fnt_parameters+fnt_digit_width,120);
  9406. lnf_long(fnt_parameters+fnt_topline_offset,-160);
  9407. lnf_long(fnt_parameters+fnt_accent,-120);
  9408. lnf_long(fnt_parameters+fnt_halfline_offset,-70);
  9409. lnf_long(fnt_parameters+fnt_overall_height,240);
  9410. lnf_long(fnt_parameters+fnt_height,-180);
  9411. lnf_long(fnt_parameters+fnt_depth,60);
  9412. lnf_long(fnt_parameters+fnt_H_height,160);
  9413. lnf_long(fnt_parameters+fnt_ex_height,120);
  9414. lnf_long(fnt_parameters+fnt_white_above,20);
  9415. lnf_long(fnt_parameters+fnt_white_below,20);
  9416.  
  9417. @ The string pool region of the font file contains the majority of the
  9418. character strings which are required to identify the font.
  9419.  
  9420. @<Complete the font file's string pool region@>=
  9421. lnf_byte(lnf_pool_offset,"0");  {Char set designator}
  9422. if using_GR then
  9423.   lnf_byte(lnf_pool_offset+1,"<")
  9424. else
  9425.   lnf_byte(lnf_pool_offset+1,"B");
  9426. lnf_byte(lnf_pool_offset+2,9);  {HT character}
  9427. for i:=4 to fnt_designator_len do
  9428.   lnf_byte(lnf_pool_offset+i-1,"Z");    {|'ZZZZ'|---full char set}
  9429. for i:=1 to fnt_type_ID_len do
  9430.   lnf_bytes(lnf_pool_offset+i-1+fnt_designator_len):=
  9431.               lnf_bytes(i-1+fnt_file_id);       {Copy first 7 of |file_ID|}
  9432. for i:=1 to fnt_type_family_len do
  9433.   lnf_bytes(lnf_pool_offset+i-1+fnt_designator_len@|+fnt_type_ID_len) :=
  9434.               " ";                      {Type family is all spaces}
  9435. for i:=1 to fnt_font_ID_len do
  9436.   lnf_bytes(lnf_pool_offset+i-1+fnt_designator_len@|+fnt_type_ID_len@|
  9437.    +fnt_type_family_len) := lnf_bytes(i-1+fnt_file_id); {Copy first 16 of |file_ID|}
  9438.  
  9439. @ The following procedure is invoked whenever an \.{LN03} font file in memory
  9440. is ready to be written to the output |ln3_file|.  Firstly we complete those
  9441. fields of the font file header region which we were unable to complete before,
  9442. storing away the total length (at front \&{and} back) and repeating the
  9443. `\.{FONT}' indication at the end of the file.  We also complete the character
  9444. definitions region descriptor, and fill in the counts of bytes loaded as
  9445. portrait, landscape and mixed mode rasters.
  9446.  
  9447. Obviously it is not possible to include the file \\{verbatim} in the output as
  9448. a binary image, since the terminal output driver and/or the printer symbiont
  9449. would probably eliminate most of those characters which are unprintable.
  9450. Therefore the LN03 requires that the binary file be encoded into ``sixel''
  9451. format; this is more usually used to transfer graphics information to DEC
  9452. printers, one sixel being a printable character (in the range `\.?'$\to$`\.\~')
  9453. which encodes the numbers |0..63| representing a column of six pixels.
  9454.  
  9455. @<Local procedures of |add_txf_to_lnf|@>=
  9456. procedure output_LNF;
  9457.   var @!i,@!j,@!k,@!records,@!sixel_ct:integer;
  9458.         @!line_out:varying [128] of char;
  9459.         @!transform : six_pack;
  9460. begin
  9461.   lnf_len:=ras_beg+ras_len+8;   {Make room for Trailing identifier}
  9462.   lnf_long(fnt_total_size,lnf_len);     {Total size noted into first longword}
  9463.   lnf_long(lnf_len-8,lnf_len);  {and into penultimate longword}
  9464.   lnf_long(lnf_len-4,lnf_longs(fnt_identifier)); {Copy |'FONT'| from 2nd longword to last}
  9465.   lnf_long(fnt_chars,ras_len);  {Length of character definitions}
  9466.   lnf_long(fnt_in_file_count,num_chars); {We might have excluded some}
  9467.   lnf_long(fnt_null_count,null_chars); {which will be reflected here}
  9468.   lnf_long(fnt_rasters,num_chars); {and so we only made this many rasters}
  9469.   lnf_long(fnt_portrait,psize);
  9470.   lnf_long(fnt_landscape,lsize);
  9471.   lnf_long(fnt_mixed,msize);
  9472.   @<Sixelize and write@>
  9473. end;
  9474.  
  9475. @ To convert eight-bit bytes to sixels, which the \.{LN03} requires, we use
  9476. (abuse?) \PASCAL's variant record mechanism, to map three bytes into 4 six-bit
  9477. characters.
  9478.  
  9479. @<Types in...@>==
  9480.   @!six_pack = packed record
  9481.     case boolean of
  9482.       false: (@!bytes : packed array [0..2] of eight_bits);
  9483.       true:  (@!sixels: packed array [1..4] of [bit(6)] 0..63)
  9484.     end;
  9485.  
  9486. @ The three bytes have to be placed in the record such that the earlier bytes
  9487. (least significant in the file) are at the left-ward end of the record.  The
  9488. resultant six-bit integers are converted to ``printable'' characters by adding
  9489. the |ASCII| character `\.{?}', thus giving characters in the range
  9490. `\.{?}'$\to$`\.{\~}'.  It is these characters that DEC calls ``sixels'', being
  9491. a representation of six pixels in one character.
  9492.  
  9493. It is most convenient to process 96 bytes at a time, thus yielding 128
  9494. characters records (of sixel characters).  Accordingly, we firstly ensure that
  9495. the file is some multiple of 96 bytes long.
  9496.  
  9497. @<Sixelize and write@>=
  9498.   while (lnf_len mod 96) <> 0 do
  9499.   begin lnf_word(lnf_len,0);    { Clear out remainder of ``last record'' }
  9500.     lnf_len:=lnf_len+2
  9501.   end;          { Can't work in longwords, in case on odd word boundary }
  9502.   records:=lnf_len div 96;
  9503.   i:=0; j:=0;           {Block/byte in |lnf| array}
  9504.   while records > 0 do
  9505.   begin
  9506.     line_out:='';               {Null string}
  9507.     sixel_ct:=0;                {We write 128 sixels (=96 bytes) per record}
  9508.     repeat
  9509.       k:=2;
  9510.       repeat
  9511.         transform.bytes[k]:=lnf[i].bytes[j];    {Store bytes from left}
  9512.         incr(j); decr(k);
  9513.         while j>=512 do
  9514.         begin incr(i); j:=j-512 end
  9515.       until k<0;                                        {3 bytes at a time}
  9516.       for k:=4 downto 1 do
  9517.       begin
  9518.         line_out:=line_out+chr(transform.sixels[k]+"?");
  9519.         incr(sixel_ct)
  9520.       end;
  9521.     until sixel_ct=128;
  9522.     write_ln(ln3_file,line_out);
  9523.     decr(records)
  9524.   end
  9525.  
  9526. @* Skipping pages.
  9527. A routine that's much simpler than |do_page| is used to pass over
  9528. pages that are not being translated. The |skip_pages| subroutine
  9529. is assumed to begin just after the preamble has been read, or just
  9530. after a |bop| has been processed. It continues until either finding a
  9531. |bop| that matches the desired starting page specifications, or until
  9532. running into the postamble.
  9533.  
  9534. In this version of \.{DVItoLN03}, it is necessary to ``read'' through all
  9535. the pages twice; on the first pass, we gather statistics about the usage
  9536. of each character in every font, to minimize the number of rasters
  9537. which have to be loaded into the LN03.  This procedure |skip_pages| has
  9538. therefore been modified to permit its use during this scan.
  9539.  
  9540. In such use, it is first called in the normal fashion, truly to skip pages
  9541. until |start_match| indicates that the first page to be output has been
  9542. found (or the beginning of the postamble, in the event that the specified
  9543. first page does not exist).  This results in |started| becoming |true|.
  9544. A further call of |skip_pages|, with |started| asserted, records the use of
  9545. each character.  Whilst this ``scanning'' phase is
  9546. taking place, the Boolean |scanning| inhibits writing to the \.{TYP} file.
  9547.  
  9548. @p procedure skip_pages;
  9549. label 9999; {end of this subroutine}
  9550. var p:integer; {a parameter}
  9551. @!k:0..255; {command code}
  9552. @!down_the_drain:integer; {garbage}
  9553. @!pages_counted:integer; {counts ``wanted'' pages whilst usage is calculated }
  9554. begin
  9555. pages_counted:=1; {We've already met the first page requested}
  9556. while true do
  9557.   begin if eof(dvi_file) then bad_dvi('the file ended prematurely');
  9558. @:Bad DVI file the file ended prematurely}{\quad\.{the file ended prematurely}@>
  9559.   k:=get_byte;
  9560.   p:=first_par(k);
  9561.   case k of
  9562.   bop: begin @<Pass a |bop| command, setting up the |count| array@>;
  9563.     if not started and start_match then
  9564.       begin started:=true; goto 9999;
  9565.       end;
  9566.       if started then
  9567.       begin incr(pages_counted); {We've got another page that's wanted}
  9568.         if (pages_counted mod 10)=0 then monitor('.'); {show progress}
  9569.         if pages_counted>max_pages then {We can skip to the postamble}
  9570.         begin old_backpointer:=first_backpointer;
  9571.           move_to_byte(post_loc);
  9572.           monitor('done.'+crlf); term_offset:=0
  9573.         end;
  9574.       end;
  9575.     end;
  9576.   set_rule,put_rule: down_the_drain:=signed_quad;
  9577.   fnt_def1,fnt_def1+1,fnt_def1+2,fnt_def1+3: begin define_font(p);
  9578.     print_ln(' ');
  9579.     end;
  9580.   xxx1,xxx1+1,xxx1+2,xxx1+3: while p>0 do
  9581.     begin down_the_drain:=get_byte; decr(p);
  9582.     end;
  9583.   post: begin in_postamble:=true;
  9584.     monitor('done.'+crlf); term_offset:=0;
  9585.     goto 9999;
  9586.     end;
  9587.   @<Cases that would set characters on paper@>;
  9588.   @<Cases that would change fonts@>;
  9589.   othercases do_nothing
  9590.   endcases;
  9591.   end;
  9592. 9999:end;
  9593.  
  9594. @ If (!) we meet a command that would require a character to be set on the
  9595. paper, we note that the relevant character has been used, provided the page
  9596. being ``skipped'' is one that is to be printed (\\{i.e.} |started| has been
  9597. set).  The |pages_counted| variable is used to inhibit the gathering of such
  9598. statistics after the specified number of pages have been ``read''.
  9599.  
  9600. We mark both the individual glyph and the font itself as being |wanted|.
  9601.  
  9602. @<Cases that would set characters on paper@>=
  9603. sixty_four_cases(set_char_0),sixty_four_cases(set_char_0+64), @/
  9604. four_cases(set1),four_cases(put1): @/
  9605.   if started and (pages_counted<=max_pages) then
  9606.   begin
  9607.     cur_font_glyph(p).loaded:=wanted;
  9608.     font_type[cur_font]:=wanted
  9609.   end
  9610.  
  9611. @ Whenever a font change would have occurred, we determine the |cur_font| so
  9612. that the above section, which notes the use of a character, can relate this to
  9613. the correct font.
  9614.  
  9615. @<Cases that would change fonts@>=
  9616. sixty_four_cases(fnt_num_0),four_cases(fnt1): @/
  9617.   begin font_num[nf]:=p; cur_font:=0;
  9618.     while font_num[cur_font]<>p do incr(cur_font);
  9619.     cur_font:=font_map[cur_font];
  9620.     cur_base:=glyph_base[cur_font]
  9621.   end
  9622.  
  9623. @ Global variables called |old_backpointer| and |new_backpointer|
  9624. are used to check whether the back pointers are properly set up.
  9625. Another one tells whether we have already found the starting page.
  9626.  
  9627. @<Glob...@>=
  9628. @!old_backpointer:integer; {the previous |bop| command location}
  9629. @!new_backpointer:integer; {the current |bop| command location}
  9630. @!started:boolean; {has the starting page been found?}
  9631.  
  9632. @ @<Set init...@>=
  9633. old_backpointer:=-1; started:=false;
  9634.  
  9635. @ @<Pass a |bop|...@>=
  9636. new_backpointer:=cur_loc-1; incr(page_count);
  9637. for k:=0 to 9 do count[k]:=signed_quad;
  9638. if signed_quad<>old_backpointer
  9639.   then error('backpointer in byte ',cur_loc-4:1,
  9640.     ' should be ',old_backpointer:1,'!');
  9641. @:Error: backpointer in byte should be}{\quad\.{backpointer...should be...}@>
  9642. old_backpointer:=new_backpointer
  9643.  
  9644. @* Using the backpointers.
  9645. The routines in this section of the program are brought into play only
  9646. if |random_reading| is |true|.
  9647. First comes a routine that illustrates how to find the postamble quickly.
  9648.  
  9649. @<Find the postamble, working back from the end@>=
  9650. n:=dvi_length;
  9651. if n<53 then bad_dvi('only ',n:1,' bytes long');
  9652. @:Bad DVI file only n bytes long}{\quad\.{only $n$ bytes long}@>
  9653. m:=n-1;
  9654. repeat if m=0 then bad_dvi('no 223s'); {ignore trailing zeroes}
  9655. @:Bad DVI file no 223s}{\quad\.{no 223s}@>
  9656.   move_to_byte(m); k:=get_byte; decr(m);
  9657. until k<>0;
  9658. repeat if m=0 then bad_dvi('all 223s');
  9659. @:Bad DVI file all 223s}{\quad\.{all 223s}@>
  9660. move_to_byte(m); k:=get_byte; decr(m);
  9661. until k<>223;
  9662. if k<>id_byte then bad_dvi('ID byte is ',k:1);
  9663. @:Bad DVI file ID byte is wrong}{\quad\.{ID byte is wrong}@>
  9664. move_to_byte(m-3); q:=signed_quad;
  9665. if (q<0)or(q>m-33) then bad_dvi('post pointer ',q:1,' at byte ',m-3:1);
  9666. @:Bad DVI file post pointer is wrong}{\quad\.{post pointer is wrong}@>
  9667. move_to_byte(q); k:=get_byte;
  9668. if k<>post then bad_dvi('byte ',q:1,' is not post');
  9669. @:Bad DVI file byte n is not post}{\quad\.{byte $n$ is not }\\{post}@>
  9670. post_loc:=q; first_backpointer:=signed_quad
  9671.  
  9672. @ Note that the last steps of the above code save the locations of the
  9673. the |post| byte and the final |bop|.  We had better declare these global
  9674. variables, together with another one that we will need shortly.
  9675.  
  9676. @<Glob...@>=
  9677. @!post_loc:integer; {byte location where the postamble begins}
  9678. @!first_backpointer:integer; {the pointer following |post|}
  9679. @!start_loc:integer; {byte location of the first page to process}
  9680.  
  9681. @ The next little routine shows how the backpointers can be followed
  9682. to move through a \.{DVI} file in reverse order. Ordinarily a \.{DVI}-reading
  9683. program would do this only if it wants to print the pages backwards or
  9684. if it wants to find a specified starting page that is not necessarily the
  9685. first page in the file; otherwise it would of course be simpler and faster
  9686. just to read the whole file from the beginning.
  9687.  
  9688. @<Count the pages and move to the starting page@>=
  9689. monitor('Finding starting page...');
  9690. @.Finding starting page@>
  9691. q:=post_loc; p:=first_backpointer; start_loc:=-1;
  9692. if p<0 then in_postamble:=true
  9693. else  begin repeat
  9694.     {now |q| points to a |post| or |bop| command; |p>=0| is prev pointer}
  9695.     if p>q-46 then
  9696.       bad_dvi('page link ',p:1,' after byte ',q:1);
  9697. @:Bad DVI file page link wrong}{\quad\.{page link wrong...}@>
  9698.     q:=p; move_to_byte(q); k:=get_byte;
  9699.     if k=bop then incr(page_count)
  9700.     else bad_dvi('byte ',q:1,' is not bop');
  9701. @:Bad DVI file byte n is not bop}{\quad\.{byte $n$ is not }\\{bop}@>
  9702.     for k:=0 to 9 do count[k]:=signed_quad;
  9703.     if start_match then start_loc:=q;
  9704.     p:=signed_quad;
  9705.     @<Note whether we've two \\{recto} pages in succession@>;
  9706.   until p<0;
  9707.   if start_loc<0 then abort('starting page number could not be found!');
  9708. @:fatal error starting page number}{\quad\.{starting page number...}@>
  9709.   move_to_byte(start_loc+1); old_backpointer:=start_loc;
  9710.   for k:=0 to 9 do count[k]:=signed_quad;
  9711.   p:=signed_quad; started:=true;
  9712.   end;
  9713. if page_count<>total_pages then
  9714.   warning('there are really ',page_count:1,' pages, not ',total_pages:1,'!');
  9715. @:Warning: there are really n pages}{\quad\.{there are really $n$ pages}@>
  9716. monitor('found.'+crlf); term_offset:=0
  9717.  
  9718. @ One of the reasons for using the above routine (which seems rather
  9719. wasteful if the user wants to print pages starting from the beginning of the
  9720. \.{DVI} file) is so that we can determine whether any particular \\{recto}
  9721. page is followed by another, without an intervening \\{verso} page.  This
  9722. information can then be used at printing time to change the printing mode of
  9723. the DEClaser~2200 (LN06), which supports duplex printing, such that the
  9724. first of these pages be printed in simplex mode.  Doing this speeds up the
  9725. printing cycle compared with merely outputting a blank \\{verso} page,
  9726. since the mechanical operation of turning over the paper is thereby avoided.
  9727.  
  9728. Therefore we construct an array of pointers into the \.{DVI} file where an
  9729. entry indicates that the page is followed by a blank \\{verso} sheet.  Here
  9730. are the necessary data structures:
  9731.  
  9732. @<Glob...@>=
  9733. @!blank_follows:array[1..max_blank_pages] of integer;
  9734. @!ptr_blanks:0..max_blank_pages;
  9735. @!next_is_recto:boolean;
  9736.  
  9737. @ Naturally we initialize this such that a final \\{recto} page will have
  9738. its blank \\{verso} side treated by the same mechanism.
  9739.  
  9740. @<Set init...@>=
  9741. next_is_recto:=true; ptr_blanks:=0;
  9742.  
  9743. @ As we work our way backwards through the file, we have the opportunity to
  9744. recognize that a \\{recto} page is followed directly by another of the same,
  9745. without an intervening \\{verso} page.  This code relies upon \TeX\ having
  9746. used its \.{\\count0} for the page number, which is the usual convention,
  9747. even when other counters are used to provide additional information in page
  9748. numbering (such as the chapter number).  When we later process the
  9749. information in the |blank_follows| array, we do so in reverse order, which
  9750. therefore corresponds with the processing of the file in the forward
  9751. direction.
  9752.  
  9753. @<Note whether we've two \\{recto} pages in succession@>=
  9754. if odd_page and next_is_recto then
  9755.   if ptr_blanks=max_blank_pages then
  9756.     capacity_exceeded('too many blank verso pages')
  9757. @:capacity exceeded too many blank verso}{\quad\.{too many blank }\\{verso}\.{ pages}@>
  9758.   else
  9759.   begin
  9760.     incr(ptr_blanks); blank_follows[ptr_blanks]:=cur_loc
  9761.   end;
  9762. next_is_recto:=odd_page
  9763.  
  9764. @* Reading the postamble.
  9765. Now imagine that we are reading the \.{DVI} file and positioned just
  9766. four bytes after the |post| command. That, in fact, is the situation,
  9767. when the following part of \.{DVItoLN03} is called upon to read, translate,
  9768. and check the rest of the postamble.
  9769.  
  9770. @p procedure read_postamble;
  9771. var k:integer; {loop index}
  9772. @!p,@!q,@!m:integer; {general purpose registers}
  9773. begin post_loc:=cur_loc-5;
  9774. print_ln('Postamble starts at byte ',post_loc:1,'.');
  9775. @.Postamble starts at byte n@>
  9776. if signed_quad<>numerator then
  9777.   warning('numerator doesn''t match the preamble!');
  9778. @:warning numerator doesn't match}{\quad\.{numerator doesn't match...}@>
  9779. if signed_quad<>denominator then
  9780.   warning('denominator doesn''t match the preamble!');
  9781. @:warning denominator doesn't match}{\quad\.{denominator doesn't match...}@>
  9782. if signed_quad<>mag then if new_mag=0 then
  9783.   warning('magnification doesn''t match the preamble!');
  9784. @:warning magnification doesn't match}{\quad\.{magnification doesn't match...}@>
  9785. max_v:=signed_quad; max_h:=signed_quad;@/
  9786. max_v_saved:=max_v; max_h_saved:=max_h;@/
  9787. print('maxv=',max_v:1,', maxh=',max_h:1);@/
  9788. max_s:=get_two_bytes; total_pages:=get_two_bytes;@/
  9789. print_ln(', maxstackdepth=',max_s:1,', totalpages=',total_pages:1);
  9790. @<Process the font definitions of the postamble@>;
  9791. @<Make sure that the end of the file is well-formed@>;
  9792. end;
  9793.  
  9794. @ When we get to the present code, the |post_post| command has
  9795. just been read.
  9796.  
  9797. @<Make sure that the end of the file is well-formed@>=
  9798. q:=signed_quad;
  9799. if q<>post_loc then
  9800.   error('bad postamble pointer in byte ',cur_loc-4:1,'!');
  9801. @:Error: bad postamble pointer}{\quad\.{bad postamble pointer}@>
  9802. m:=get_byte;
  9803. if m<>id_byte then error('identification in byte ',cur_loc-1:1,
  9804. @:Error: identification in byte p should be n}{\quad\.{identification...should be $n$}@>
  9805.     ' should be ',id_byte:1,'!');
  9806. k:=cur_loc; m:=223;
  9807. while (m=223)and not eof(dvi_file) do m:=get_byte;
  9808. if not eof(dvi_file) and (m<>0) then bad_dvi('signature in byte ',cur_loc-1:1,
  9809. @:Bad DVI file signature...should be}{\quad\.{signature...should be...}@>
  9810.     ' should be 223')
  9811. else if cur_loc<k+4 then
  9812.   warning('not enough signature bytes at end of file (',
  9813. @:Warning: not enough signature bytes}{\quad\.{not enough signature bytes...}@>
  9814.     cur_loc-k:1,')');
  9815.  
  9816. @ @<Process the font definitions...@>=
  9817. repeat k:=get_byte;
  9818. if (k>=fnt_def1)and(k<fnt_def1+4) then
  9819.   begin p:=first_par(k);
  9820.     define_font(p);
  9821.     if not substitution_pass then print_ln(' ');
  9822.     k:=nop;
  9823.   end;
  9824. until k<>nop;
  9825. if k<>post_post then
  9826.   error('byte ',cur_loc-1:1,' is not postpost!')
  9827. @:Error: byte n is not postpost}{\quad\.{byte $n$ is not }\\{postpost}@>
  9828.  
  9829. @* The main program.
  9830. Now we are ready to put it all together. This is where \.{DVItoLN03} starts,
  9831. and where it ends.  These macros define various possible exit values for the
  9832. program, through the VAX/VMS system service \.{\$exit}.
  9833. @^system dependencies@>
  9834.  
  9835. @d VAX_exit==@=$exit@>
  9836. @d VAX_ss_normal==@= sts$k_success @>
  9837. @d VAX_ss_warning==@= sts$k_warning + sts$m_inhib_msg @>
  9838. @d VAX_ss_error==@= sts$k_error + sts$m_inhib_msg @>
  9839. @d VAX_ss_fatal==@= sts$k_severe + sts$m_inhib_msg @>
  9840.  
  9841. @p begin initialize; {get all variables initialized}
  9842.   dialog; {set up all the options}
  9843.   write_ln(term_out,'(',actual_file_spec,crlf);
  9844.   print_ln('Reading from file ',actual_file_spec);
  9845.   @<Process the preamble@>;
  9846.   @<Find the postamble, working back from the end@>;
  9847.   in_postamble:=true; read_postamble; in_postamble:=false;
  9848.   @<Count the pages and move to the starting page@>;
  9849.   if not in_postamble then @<Gather font usage statistics@>;
  9850.   if not in_postamble then @<Translate up to |max_pages| pages@>;
  9851.   close(ln3_file,@=disposition:=save@>,VAX_continue);
  9852.   monitor(')');
  9853. final_end:
  9854.   ask := (history > spotless) or odd(VAX_cli_present('LOG'));
  9855.   if VAX_cli_present('LOG') = VAX_cli_negated then ask := false;
  9856.   if ask then
  9857.     close(type_file,@=disposition:=save@>,VAX_continue)
  9858.   else
  9859.     close(type_file,@=disposition:=delete@>,VAX_continue);
  9860.   case history of       { Issue an appropriate VAX exit status }
  9861.   spotless: VAX_exit(VAX_ss_normal);    { Everything OK! }
  9862.   warning_given: VAX_exit(VAX_ss_warning);
  9863.   error_given: VAX_exit(VAX_ss_error);
  9864.   fatal_error: VAX_exit(VAX_ss_fatal)
  9865.   end;
  9866. end.
  9867.  
  9868. @ The main program needs a few global variables in order to do its work.
  9869.  
  9870. @<Glob...@>=
  9871. @!k,@!m,@!n,@!p,@!q:integer; {general purpose registers}
  9872.  
  9873. @ A \.{DVI}-reading program that reads the postamble first need not look at the
  9874. preamble; but \.{DVItoLN03} looks at the preamble in order to do error
  9875. checking, and to display the introductory comment.
  9876.  
  9877. @<Process the preamble@>=
  9878. open_dvi_file;
  9879. p:=get_byte; {fetch the first byte}
  9880. if p<>pre then bad_dvi('First byte isn''t start of preamble!');
  9881. @:Bad DVI file First byte isn\'t}{\quad\.{First byte isn't...}@>
  9882. p:=get_byte; {fetch the identification byte}
  9883. if p<>id_byte then
  9884.   error('identification in byte 1 should be ',id_byte:1,'!');
  9885. @:Error: identification in byte p should be n}{\quad\.{identification...should be $n$}@>
  9886. @<Compute the conversion factor@>;
  9887. p:=get_byte; {fetch the length of the introductory comment}
  9888. print('''');
  9889. while p>0 do
  9890.   begin decr(p); print(xchr[get_byte]);
  9891.   end;
  9892. print_ln('''')
  9893.  
  9894. @ The conversion factor |conv| is figured as follows: There are exactly
  9895. |n/d| \.{DVI} units per decimicron, and 254000 decimicrons per inch,
  9896. and |resolution| pixels per inch. Then we have to adjust this
  9897. by the stated amount of magnification.
  9898.  
  9899. @<Compute the conversion factor@>=
  9900. numerator:=signed_quad; denominator:=signed_quad;
  9901. if numerator<=0 then bad_dvi('numerator is ',numerator:1);
  9902. @:Bad DVI file numerator is wrong}{\quad\.{numerator is wrong}@>
  9903. if denominator<=0 then bad_dvi('denominator is ',denominator:1);
  9904. @:Bad DVI file denominator is wrong}{\quad\.{denominator is wrong}@>
  9905. print_ln('numerator/denominator=',numerator:1,'/',denominator:1);
  9906. conv:=(numerator/254000.0)*(resolution/denominator);
  9907. mag:=signed_quad;
  9908. if new_mag>0 then mag:=new_mag
  9909. else if mag<=0 then bad_dvi('magnification is ',mag:1);
  9910. @:Bad DVI file magnification is wrong}{\quad\.{magnification is wrong}@>
  9911. true_conv:=conv; conv:=true_conv*(mag/1000.0);
  9912. print_ln('magnification=',mag:1,'; ',conv:16:8,' pixels per DVI unit')
  9913.  
  9914. @ The code shown here uses a convention that has proved to be useful: If the
  9915. starting page was specified as, e.g., `\.{1.*.-5}', then all page numbers in
  9916. the file are displayed by showing the values of counts 0, 1, and~2, separated
  9917. by dots. These numbers are displayed on the terminal when DVItoLN03 is working
  9918. on that page.
  9919.  
  9920. The parameter |-1| in the call of |do_page| indicates to the latter that
  9921. this is \&{not} a recursive call of the function, and hence that it should
  9922. initialize its data structures for a fresh page.
  9923.  
  9924. @<Translate up to...@>=
  9925. begin while max_pages>0 do
  9926.   begin decr(max_pages);
  9927.   print_ln(' '); print(cur_loc-45:1,': beginning of page ');
  9928.   text_out:='[';
  9929.   last_counter:=0;
  9930.   for k:=0 to 9 do if count[k]<>0 then last_counter:=k;
  9931.   for k:=0 to last_counter do
  9932.     begin
  9933.       if (k=0) or (count[k]<>0) then
  9934.       begin
  9935.         print(count[k]:1); text_out:=text_out+str_int(count[k])
  9936.       end;
  9937.       if k<last_counter then
  9938.       begin print('.'); text_out:=text_out+'.' end
  9939.       else print_ln(' ');
  9940.     end;
  9941.   monitor(text_out);    {Let user know which page has started}
  9942.   cur_loc_after_bop:=cur_loc;  {For checking on blank \\{verso} pages}
  9943.   if not do_page(-1) then bad_dvi('page ended unexpectedly');
  9944. @:Bad DVI file page ended unexpectedly}{\quad\.{page ended unexpectedly}@>
  9945.   incr(pages_printed);
  9946.   repeat k:=get_byte;
  9947.   if (k>=fnt_def1)and(k<fnt_def1+4) then
  9948.     begin p:=first_par(k); define_font(p); k:=nop;
  9949.     end;
  9950.   until k<>nop;
  9951.   write_ln(term_out,']'); incr(term_offset); {Let user know page has finished}
  9952.   if k=post then
  9953.     begin in_postamble:=true; goto done;
  9954.     end;
  9955.   if k<>bop then bad_dvi('byte ',cur_loc-1:1,' is not bop');
  9956. @:Bad DVI file byte n is not bop}{\quad\.{byte $n$ is not }\\{bop}@>
  9957.   @<Pass a |bop|...@>;
  9958.   end;
  9959. done: @<Conditionally eject last page@>
  9960. end
  9961.  
  9962. @  A string variable |text_out| is provided to hold the
  9963. characters of a page number.  We also use |last_counter| to indicate the
  9964. last significant item in the |count| array when displaying the page number.
  9965.  
  9966. @<Globals...@>=
  9967.   @!text_out : file_spec;
  9968.   @!last_counter : 0..9;
  9969.  
  9970. @ No output for the terminal occurs before the pages are processed except
  9971. for error and font load messages, all of which end with a newline.  Therefore,
  9972. we are quite safe in resetting |term_offset| during the initialisation phase.
  9973.  
  9974. @<Set initial...@>=
  9975.   term_offset:=0;
  9976.  
  9977. @* System-dependent changes.
  9978. Here are the remaining changes to the program
  9979. that are necessary to make \.{DVItoLN03} work on Vax/VMS.
  9980. @^system dependencies@>
  9981.  
  9982. We define here various variables required by the VAX.
  9983.  
  9984. \yskip\hang |history| is used to record whether any warning or error
  9985. messages (including fatal errors) have been reported.
  9986.  
  9987. \yskip\hang |type_file| and |ln3_file| are |file| variables used for writing
  9988. the log and
  9989. output files, respectively.  Advantage is taken of VAX-\PASCAL's |varying|
  9990. arrays to hold variable length strings: |file_name| is used generally for
  9991. names of files, whilst |def_file_name| initially holds the specification of
  9992. the \.{.dvi} file being processed, from which the name is later extracted for
  9993. use in forming the names of generated files.
  9994.  
  9995. The remaining variables are used during the parsing of the file
  9996. specification.
  9997.  
  9998. @<Glob...@>==
  9999. @!history: exit_status;
  10000. @!type_file: text;
  10001. @!ln3_file: text;
  10002. @!file_name,@!actual_file_spec,@!log_file_name,
  10003.    @!output_file_name:file_spec;
  10004. @!file_message:file_spec;
  10005. @!cmd_j,@!cmd_k:integer;
  10006. @!ask : boolean;
  10007.  
  10008. @ Here is the enumeration type used for |history|.
  10009.  
  10010. @<Types...@>==
  10011. @!exit_status = (@!spotless,@!warning_given,@!error_given,@!fatal_error);
  10012. @ We now do all the VMS specific things to open the files required, etc. The
  10013. \.{DVIfile} is opened with a fixed block length, and with \.{direct} access
  10014. method, to permit |random_reading|.  A |user_action| procedure is called, to
  10015. perform the actual opening of the file; this gives us access to the various
  10016. VAX data structures (|FAB|,|RAB| and |XAB|) and thus we are able to determine
  10017. the actual length of the file.
  10018.  
  10019. The |output| file (directed to the terminal) is opened without
  10020. |carriage_control|, in order that |write_ln| may be used to ensure that
  10021. each partial line of output is displayed on the screen.
  10022.  
  10023. @<Preset init...@>=
  10024. open(output,'SYS$OUTPUT',@=carriage_control:=none@>,VAX_continue);
  10025. history := spotless;  { We haven't had any errors (yet!) }
  10026. @<Get name of \.{DVI} file from command-interpreter, and open it@>@;
  10027. @<Extract just the file name from the specification of the \.{DVI} file@>@;
  10028. @<Extract the |log_file_name| from the \.{/LOG} qualifier@>@;
  10029. @<Create and open the \.{TYP} log file@>@;
  10030. @<Extract the \TeX\ Font Metric directory from the qualifiers@>;
  10031. @<Extract the font directories from the qualifiers@>@;
  10032. @<Extract the virtual font directory from the qualifiers@>@;
  10033. @<Extract the |output_file_name| from the \.{/OUTPUT} qualifier@>@;
  10034. @<Create and open the \.{LN3} output file@>@;
  10035.  
  10036. @ When we parse the qualifiers pertaining to the physical font directories,
  10037. we use the variable length strings |tex_pxl_font| and |tex_pk_font|  to hold
  10038. the directory specifications derived from the command line qualifiers
  10039. \.{/PXL\_FONT\_DIRECTORY} and \.{/PK\_FONT\_DIRECTORY}, respectively.  Iff,
  10040. after applying logical name translation (if necessary),  a file directory
  10041. specification is found to end in the sequence `\.{.]}', then a rooted file
  10042. directory structure will be assumed; this is recorded in the variables
  10043. |pk_rooted| and |pxl_rooted|.
  10044.  
  10045. There is no such complication for the \.{/TFM\_DIRECTORY} qualifier; its
  10046. value is merely copied to |tfm_directory|.
  10047.  
  10048. @<Glob...@>=
  10049. @!tex_pxl_font,@!tex_pk_font,@!tfm_directory:file_spec;
  10050. @!pxl_rooted,@!pk_rooted:boolean;
  10051.  
  10052. @ Originally, this program had the location of the \TeX\ Font Metrics files
  10053. (\.{.TFM}) `hard-wired' into it, just as the VMS implementation of \TeX\
  10054. itself did, through the logical name \.{TEX\$FONTS}.  \TeX\ has since been
  10055. modified so that such information is communicated to the program through a
  10056. command-line qualifier, and this program too now makes use of the same
  10057. mechanism.
  10058.  
  10059. This permits two things:
  10060.  
  10061. \yskip\hang$\bullet$ The site manager can chose whether to use logical names
  10062. of the form \.{TEX\UL FONTS} or \.{TEX\$FONTS} ({\sl Digital\/} recommends that
  10063. customers should \&{not} define logical names containg the `\.\$' character,
  10064. since the logicals which they themselves define \&{always} contain this
  10065. character, and may at some future date conflict with customer-defined
  10066. logicals).
  10067.  
  10068. \yskip\hang$\bullet$ Individual users may define their own logical names
  10069. that provide a search-list for a number of directories; this can prove
  10070. useful whilst testing a new font that should not be installed for general
  10071. access.
  10072.  
  10073. @<Extract the \TeX\ Font Metric directory from the qualifiers@>=
  10074. if odd(VAX_cli_present('TFM_DIRECTORY')) then
  10075.   k:=VAX_cli_get_value('TFM_DIRECTORY',tfm_directory)
  10076. else
  10077.   tfm_directory:=''
  10078.  
  10079. @ At this point we ``read'' the font directory specifications passed as the
  10080. values of the qualifiers \.{/PXL\_FONT\_DIRECTORY} and
  10081. \.{/PK\_FONT\_DIRECTORY}.  At least one of these must be provided with a
  10082. value.  The specifications are transferred to the variables |tex_pxl_font| and
  10083. |tex_pk_font| respectively, and logical name translation is applied.  If the
  10084. resultant string doesn't end with a `\.]', an error is signalled.  If the
  10085. character \&{preceding} this bracket is `\..', the final `\.]' is discarded.
  10086.  
  10087. @<Extract the font directories from the qualifiers@>==
  10088. tex_pk_font:=''; tex_pxl_font:='';
  10089. ask:=odd(VAX_cli_present('PK_FONT_DIRECTORY'));
  10090. if ask then
  10091. begin
  10092.   VAX_cli_get_value('PK_FONT_DIRECTORY',tex_pk_font);
  10093.   Translate(tex_pk_font,file_name);
  10094.   if file_name[file_name.length] <> ']' then
  10095.     abort('Bad /PK_FONT_DIRECTORY qualifier; doesn''t end with a `]''');
  10096. @:fatal error Bad pk_font_directory}{\quad\.{Bad /PK_FONT_DIRECTORY qualifier}@>
  10097.   pk_rooted := file_name[file_name.length-1] = '.';
  10098. end;
  10099. if odd(VAX_cli_present('PXL_FONT_DIRECTORY')) then
  10100. begin
  10101.   VAX_cli_get_value('PXL_FONT_DIRECTORY',tex_pxl_font);
  10102.   Translate(tex_pxl_font,file_name);
  10103.   if file_name[file_name.length] <> ']' then
  10104.     abort('Bad /PXL_FONT_DIRECTORY qualifier; doesn''t end with a `]''');
  10105. @:fatal error Bad pxl_font_directory}{\quad\.{Bad /PXL_FONT_DIRECTORY qualifier}@>
  10106.   pxl_rooted := file_name[file_name.length-1] = '.';
  10107.   ask:=true;
  10108. end;
  10109. if not ask then abort('Where are the font directories? (/PK_FONT_DIRECTORY, etc)');
  10110. @:fatal error Where are the font directories}{\quad\.{Where are the font directories?}@>
  10111.  
  10112. @ Similarly, we determine the directory in which virtual font metrics are to
  10113. be found; if the qualifier \.{/VIRTUAL\UL DIRECTORY} is negated, then
  10114. virtual font support is explicitly suppressed.
  10115.  
  10116. @<Extract the virtual font directory from the qualifiers@>==
  10117. tex_virtual:='';
  10118. i := VAX_cli_present('VIRTUAL_DIRECTORY');
  10119. no_virt_support := i = VAX_cli_negated;
  10120. if odd(i) then
  10121.   VAX_cli_get_value('VIRTUAL_DIRECTORY',tex_virtual);
  10122.  
  10123. @ The variable length string |tex_virtual| receives this definition.  If
  10124. \.{/NOVIRTUAL\UL DIRECTORY} has been specified, this is recorded in
  10125. |no_virt_support|.
  10126.  
  10127. @<Glob...@>=
  10128. @!tex_virtual:file_spec;
  10129. @!no_virt_support:boolean;
  10130.  
  10131. @ We now ask the Vax/VMS command-line interpreter to give us the file
  10132. specification furnished by the user to the command which invokes DVItoLN03.
  10133. The file is opened \.{readonly} to preclude any possibility of corrupting or
  10134. deleting it, and the \.{user\_action} paramter of the VAX-\PASCAL\ |open|
  10135. procedure is used, so that we can determine the full file specification and
  10136. length of the file, which is also opened for random (\.{direct}) access.
  10137.  
  10138. We can't use |abort| for the fatal error reports, because the log file
  10139. hasn't yet been opened, so we define this instead:
  10140.  
  10141. @d bomb_out(#)==begin write_ln(output,crlf,'Fatal error: ',#,crlf); jump_out end
  10142.  
  10143. @<Get name of \.{DVI} file from command-interpreter, and open it@>=
  10144. def_file_name:='';
  10145. ask:=odd(VAX_cli_get_value('FILESPEC',def_file_name));
  10146. if not ask then bomb_out('No file name provided');
  10147. @.No file name provided@>
  10148. @:fatal error no file name provided}{\quad\.{No file name provided}@>
  10149. open(dvi_file,def_file_name,@=readonly@>,@=access_method:=direct@>,
  10150.         @=user_action@>:=file_open,@=default@>:='.DVI;0',VAX_continue);
  10151. ask:=status(dvi_file)<>0; dvi_size:=file_len;
  10152. if ask then bomb_out('Couldn''t open ',def_file_name);
  10153. @:fatal error couldn\'t open DVI}{\quad\.{Couldn't open .DVI file}@>
  10154. actual_file_spec:=def_file_name;
  10155.  
  10156. @ The user has the option to specify an explicit file name on the \.{/LOG}
  10157. qualifier.  If none is specified, or the qualifier is absent, the log file
  10158. will be created in the current directory, with the same name as the input
  10159. file and a file extension of `\.{.TYP}'.  These are also the defaults used
  10160. if the relevant portion of the file specification is not given with the
  10161. \.{/LOG} qualifier.
  10162.  
  10163. @<Extract the |log_file_name| from the \.{/LOG} qualifier@>=
  10164. log_file_name:='';  {Null name specified in case defaults required}
  10165. if odd(VAX_cli_present('LOG')) then
  10166.   VAX_cli_get_value('LOG',log_file_name);
  10167.  
  10168. @ After we've successfully opened the \.{DVI} file (and are thus assured that
  10169. it exists), we strip off any node, device or directory specification from the
  10170. front of the string, and also remove any trailing file type or version number
  10171. specification.  This makes use of the VAX-\PASCAL\ |substr| function, but
  10172. could readily be recoded into standard \PASCAL.
  10173.  
  10174. @<Extract just the file name from the specification of the \.{DVI} file@>=
  10175. cmd_j:=1;  {Strip off any leading node, device or directory name}
  10176. for cmd_k:=1 to def_file_name.length do
  10177.   if (def_file_name[cmd_k]=']')
  10178.     or (def_file_name[cmd_k]=':')
  10179.       or (def_file_name[cmd_k]='>')
  10180.         then cmd_j:=cmd_k+1;
  10181. if cmd_j<=def_file_name.length then
  10182.   def_file_name:=substr(def_file_name,cmd_j,def_file_name.length-cmd_j+1);@/
  10183. cmd_j:=0; {and then strip off any trailing file type or version indication}
  10184. for cmd_k:=1 to def_file_name.length do
  10185.   if (cmd_j=0) and @/
  10186.     ((def_file_name[cmd_k]='.') or (def_file_name[cmd_k]=';')) then
  10187.       cmd_j:=cmd_k;
  10188. if cmd_j=0 then cmd_j:=def_file_name.length+1;
  10189. def_file_name:=substr(def_file_name,1,cmd_j-1);
  10190.  
  10191. @ We now |open| the \.{TYP} file in the current directory, to act as a log
  10192. file for this run of DVItoLN03.  The file is opened with |disposition:=save|
  10193. so that it will still `exist' even in the event of a crash; this means that the
  10194. user can interrupt the program (with Ctrl-Y) and be assured that the only
  10195. file that will be retained is the log.
  10196.  
  10197. @<Create and open the \.{TYP} log file@>=
  10198. file_name:=def_file_name+'.TYP';
  10199. open(type_file,log_file_name,@=new@>,@=32767@>,@=disposition:=save@>,
  10200.         @=default@>:=file_name,VAX_continue);
  10201. ask:=status(type_file)>0;
  10202. if ask then bomb_out('Couldn''t create ',file_name);
  10203. @:fatal error couldn\'t create TYP}{\quad\.{Couldn't create .TYP file}@>
  10204. rewrite(type_file);
  10205.  
  10206. @ A particularly useful qualifier for those with limited disk quota is
  10207. \.{/OUTPUT}, which permits a user to specify a different output file
  10208. specification from the default, which is to open the output file in the
  10209. current directory with a name taken from the input (\.{.DVI}) file, and file
  10210. extension `\.{.LN3}'.  Using this qualifier permits a user to divert the
  10211. output to a scratch disk.
  10212.  
  10213. @<Extract the |output_file_name| from the \.{/OUTPUT} qualifier@>=
  10214. output_file_name := ''; {Prepare to use the default name}
  10215. if odd(VAX_cli_present('OUTPUT')) then
  10216.   VAX_cli_get_value('OUTPUT',output_file_name);
  10217.  
  10218. @ Finally, we create and |open| the \.{LN3} file.  Again this is opened with
  10219. |disposition:=delete|.
  10220.  
  10221. @<Create and open the \.{LN3} output file@>=
  10222. file_name := def_file_name+'.LN3';
  10223. open(ln3_file,output_file_name,@=new@>,@=256@>,@=disposition@>:=@=delete@>,
  10224.                 @=carriage_control@>:=@=none@>,
  10225.                 @=default@>:=file_name,VAX_continue);
  10226. ask:=status(ln3_file)>0;
  10227. if ask then begin
  10228.   abort('Couldn''t create ',file_name);
  10229. @:fatal error couldn\'t create LN3 file}{\quad\.{Couldn't create .LN3 file}@>
  10230.   goto final_end
  10231. end;
  10232. rewrite(ln3_file);
  10233.  
  10234. @ Whenever a binary file is opened, the optional |user_action| parameter is
  10235. provided to the call of the VAX-\PASCAL\ |open| procedure; this therefore
  10236. invokes the procedure |file_open| (in the next section) which is therefore
  10237. able to determine the full specification of the file opened, and also its
  10238. exact length; the former is placed into |def_file_name|, and the latter in
  10239. |file_len|.
  10240.  
  10241. The following definitions permit more \PASCAL-like references to the VAX/RMS
  10242. data structures within |file_open|
  10243.  
  10244. @d VAX_FAB_type == @=fab$type@>
  10245. @d VAX_RAB_type == @=rab$type@>
  10246. @d VAX_NAM_type == @=nam$type@>
  10247. @d VAX_XAB_type == @=xab$type@>
  10248. @d VAX_RMS_open == @=$OPEN@>
  10249. @d VAX_RMS_connect == @=$CONNECT@>
  10250. @d VAX_fab_to_nam == @=fab$l_nam@>      {Pointer in FAB to NAM block}
  10251. @d VAX_nam_length == @=nam$b_rsl@>      {Length of resultant file name}
  10252. @d VAX_nam_address == @=nam$l_rsa@>     {and its whereabouts}
  10253. @d VAX_fab_to_xab == @=fab$l_xab@>      {Pointer in FAB to first XAB}
  10254. @d VAX_xab_code == @=xab$b_cod@>        {Byte in XAB which identifies its type}
  10255. @d VAX_xab_fhc == @=xab$c_fhc@>         {XAB contains file header characteristics}
  10256. @d VAX_xab_next == @=xab$l_nxt@>        {Pointer to another XAB}
  10257. @d VAX_end_block == @=xab$l_ebk@>       {Number of file block containing EOF}
  10258. @d VAX_first_free == @=xab$w_ffb@>      {First free byte in last block}
  10259.  
  10260. @<Glob...@>=
  10261. @!def_file_name : file_spec;
  10262. @!file_len : integer;
  10263.  
  10264. @ Here is the procedure |file_open|, which determines the actual length of
  10265. the file being opened.  It also extracts the full file specification of the
  10266. \.{DVI} which it opens, and reports this back in |def_file_name|.
  10267.  
  10268. Vax/VMS uses RMS (Record Management Services) to handle file operations, and
  10269. this utilizes various structures, in particular, the FAB (File Access Block)
  10270. and the RAB (Record Access Block).  There are also a number of different
  10271. XABs (eXtension Access Block), one of which contains the required
  10272. information relating to the size of the file.
  10273.  
  10274. A |user_action| procedure such as this is invoked by the VAX-\PASCAL\ |open|
  10275. procedure after it has set up the FAB and RAB structures.  The user is
  10276. therefore able at that time to change these structures to alter the processing
  10277. algorithms used by RMS.  The file has \&{not}, however, been opened at that
  10278. time; it is necessary to call the RMS \.{\$OPEN} service.  Once the RAB has
  10279. been completed to the user's satisfaction, the \.{\$CONNECT} service is called
  10280. to associate the file variable with the records of the file.
  10281.  
  10282. Due to the nonsensical manner in which the |FAB|, etc., are declared in
  10283. \.{SYS\$LIBRARY:STARLET.PAS}, wherein the pointers to |XAB|s are declared
  10284. to be of type |unsigned|, it is necessary for us to use type casts
  10285. for assignments and usage as a pointer!
  10286.  
  10287. @<Procedures for init...@>=
  10288. function file_open(var @!fab:VAX_FAB_type; var @!rab:VAX_RAB_type;
  10289.                 var @!f_spec:byte_file): integer;
  10290. type @!xab_ptr = ^VAX_XAB_type;
  10291.      @!nam_ptr = ^VAX_NAM_type;
  10292.      @!char_ptr = ^char;
  10293. var @!status:integer;@/
  10294.     @!nam : nam_ptr;
  10295.     @!xab : xab_ptr;
  10296.     @!actual_length : integer;
  10297.     @!next : char_ptr;
  10298.     @!i : integer;
  10299. begin file_len:=-1;     {In case we can't determine actual file size}
  10300.   status:=VAX_RMS_open(fab);    {Fill in the FAB and RAB}
  10301.   if odd(status) then
  10302.   begin status:=VAX_RMS_connect(rab);   {Link the RAB to RMS}
  10303.     if odd(status) then
  10304.     begin
  10305.       nam:=fab.VAX_fab_to_nam::nam_ptr;
  10306.       if nam<>nil then
  10307.       begin
  10308.         actual_length := nam^.VAX_nam_length;
  10309.         def_file_name := '';    {Reset to empty}
  10310.         next:=nam^.VAX_nam_address::char_ptr;
  10311.         for i:=1 to actual_length do
  10312.         begin
  10313.           def_file_name := def_file_name + next^;
  10314.           next:=(next::integer + 1)::char_ptr;
  10315.         end;
  10316.       end;
  10317.       xab:=fab.VAX_fab_to_xab::xab_ptr;
  10318.       {Find the File Header Characteristics}
  10319.       while (xab<>nil) and (xab^.VAX_xab_code<>VAX_xab_fhc) do
  10320.         xab:=xab^.VAX_xab_next::xab_ptr;
  10321.       if xab<>nil then
  10322.         with xab^ do
  10323.           file_len:=int((VAX_end_block-1)*VAX_block_length+VAX_first_free);
  10324.     end;
  10325.   end;
  10326.   file_open:=status;    {Return success or failure}
  10327. end; {|file_open|}
  10328.  
  10329. @ This function provides us with logical name translation.  It utilizes the
  10330. \.{SYS\$TRNLNM} system service in preference to the obsolete \.{SYS\$TRNLOG}.
  10331.  
  10332. On entry, any terminal `\.:' on the string |log_name| is discarded before
  10333. attempting logical name translation.  If the latter succeeds, the translation
  10334. is written back to |equivalence_string| and the function returns |true|; if
  10335. translation fails, the original |log_name| is returned in |equivalence_string| and
  10336. |false| returned.
  10337.  
  10338. It is necessary to provide the local variable |copy_name| during the
  10339. translation process, because VAX-\PASCAL\ does not permit the address of a
  10340. |var| parameter to be taken, so we cannot use |equivalence_string| to receive
  10341. the successive translations, and yet the address of the string must be passed
  10342. to the system service as the buffer to receive the translated logical name.
  10343. Similarly, we cannot use |log_name|, since that will be a local copy, so that
  10344. |string_size| will be that of the original logical name.
  10345.  
  10346. The next macro is used to hide the VAX-\PASCAL\ conformant schema from W{\mc
  10347. EAVE}, and thus permit more meaningful formatting.
  10348.  
  10349. @d VAX_conformant_schema(#)==VAX_volatile varying [#] of char
  10350. @#
  10351. @d VAX_trnlnm == @=$TRNLNM@>
  10352. @d VAX_lnm_case_blind==@= lnm$m_case_blind@>
  10353.  
  10354. @<Procedures for initialization@>==
  10355. function Translate(log_name : VAX_conformant_schema(string_size);
  10356.    var equivalence_string : VAX_conformant_schema(buf_size) ) : boolean;
  10357.   var @!i, @!status : integer;
  10358.       @!copy_name : VAX_volatile file_spec;
  10359.       @!return_length : VAX_volatile integer;
  10360.       @!attributes : unsigned;
  10361.       @!item_list : VAX_volatile array [0..1] of VMS_item_list;
  10362. begin
  10363.   equivalence_string := log_name;  {In case translation fails}
  10364.   copy_name := log_name;
  10365.   if copy_name[copy_name.length] = ':' then
  10366.     decr(copy_name.length);
  10367.   attributes := VAX_lnm_case_blind;
  10368.   return_length := 0;
  10369.   with item_list[0] do
  10370.   begin
  10371.     buffer_length := buf_size;
  10372.     item_code := @=LNM$_STRING@>;
  10373.     buffer_addr := @=iaddress@>(copy_name.body);
  10374.     ret_len_addr := @=iaddress@>(return_length);
  10375.   end;
  10376.   item_list[1].next_item := 0;
  10377.   status := VAX_trnlnm(attributes,'LNM$DCL_LOGICAL',copy_name,,item_list);
  10378.   if not odd(status) then
  10379.     Translate := false
  10380.   else
  10381.   begin
  10382.     copy_name.length := return_length;
  10383.     return_length := 0;
  10384.     while odd(VAX_trnlnm(attributes,'LNM$DCL_LOGICAL',copy_name,,item_list)) do
  10385.     begin
  10386.       copy_name.length := return_length;
  10387.       return_length := 0;
  10388.     end;
  10389.     Translate := true;
  10390.     equivalence_string := copy_name;
  10391.   end;
  10392. end;
  10393.  
  10394. @ Here are the new types introduced within that function.  Many Vax/VMS system
  10395. services make use of an |item_list| to pass information in and out.  An
  10396. |item_list| consists of a number of |item_list| elements, with each element
  10397. containing the following fields:
  10398.  
  10399. \centerline{\vtop{\offinterlineskip\hrule
  10400.   \halign{\vrule#&\ \strut#\hfil\ &\ #\hfil\ &\ #\hfil\ &\vrule#\cr
  10401.     height2pt&\omit&\omit&\omit&\cr
  10402.     &\hfil Name & \hfil Type & \hfil Usage&\cr
  10403.     height2pt&\omit&\omit&\omit&\cr
  10404.     \noalign{\hrule}
  10405.     height2pt&\omit&\omit&\omit&\cr
  10406.     &|buffer_length| & 16-bit word & Size of buffer&\cr
  10407.     &|item_code| & unsigned 16-bit word & Code for desired operation&\cr
  10408.     &|buffer_address| & Pointer to char & Address of buffer&\cr
  10409.     &|ret_len_addr| & Pointer to integer & To receive length of
  10410.       translation&\cr
  10411.     height2pt&\omit&\omit&\omit&\cr}
  10412.   \hrule
  10413. }}\yskip
  10414.  
  10415. \noindent This structure is overlaid with a single 32-bit integer whose use is
  10416. solely to hold the value zero indicating the end of the list.
  10417.  
  10418. @<Types in the...@>==
  10419.   @!VMS_item_list =
  10420.     packed record
  10421.       case boolean of
  10422.       true: (
  10423.         @!buffer_length : sixteen_bits;@/
  10424.         @!item_code     : sixteen_bits;@/
  10425.         @!buffer_addr   : integer;@/
  10426.         @!ret_len_addr  : integer);
  10427.       false: (
  10428.         @!next_item : integer)
  10429.     end;
  10430.  
  10431. @* Index.
  10432. Pointers to error messages appear here together with the section numbers
  10433. where each ident\-i\-fier is used.
  10434.